﻿'Required references:

'Built-in
'System.DirectoryServices
'System.DirectoryServices.AccountManagement
'System.Management

'In C:\Program Files\Microsoft Configuration Manager Console\AdminUI\bin
'Microsoft.ConfigurationManagement.ManagementProvider
'adminui.wqlqueryengine.dll
'Had to copy these to \\ds.ad.ssmhc.com\ssmdfs\IC\UserHome\MBROAD\My Documents\Prog\SMSCollectionSpeedup-Service\Setup1???

'In C:\Program Files\Microsoft System Center Configuration Manager 2007 SDK\Redistributables
'Should be on the COM tab when adding references
'smsrsgenctl.dll

'GroupExceptionsList = List from text file - No Domain prefix
'DictGroupsModifiedRecently = (NETBIOSDOMAINNAME & "\" & sObjectName, sObjectDN)  - Has Domain Prefix
'GroupsToCollections = (GroupName, sCollectionID) - Has Domain prefix since the Collection query has the prefix

'TODO LIST
'Make sure we aren't even querying AD for metadata unless it is a Group with an associated collection
'Logfile location still hardcoded
'Review ALL Try/Catches and add new ones as needed to get better logging on errors
'Create a "computer" class for the info about each computer? maybe not worth it since we create the DDR as we find the info

'test Ping/WMI connect to Server first-if they fail dont update TimeDiff file and wait til next loop to try again (and log error)
'Maybe only update timediff file at the very end of the loop
'Set a MAXTIMEDIFF value in the Reg and make that the max even if the TimeDiff file is older
'CHeck to make sure SQL group/collection function returned at least one record
'Check for nested groups memberships to compare against Collection query rules (i.e. xpsp3/ie7)
'Separate function based just upon groups that changed

Option Explicit On
Option Strict On
Imports System.IO
Imports Microsoft.Win32
'Imports System.Math
'Imports System.Management
Imports System.Data.SqlClient
'Imports System.Collections
'Imports System.Collections.Generic
Imports System.DirectoryServices
Imports System.Text
Imports Microsoft.ConfigurationManagement.ManagementProvider
Imports Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine
'Imports Microsoft.ConfigurationManagement.ManagementProvider.ConnectionManagerBase
'Imports Microsoft.ConfigurationManagement.ManagementProvider.SmsNamedValuesDictionary
'Imports System.DirectoryServices.AccountManagement
Imports System.Timers
Imports SMSRSGENCTLLib
Imports System.Management

'Imports System.Security.Principal
'Imports System.Windows.Forms.Form
'Imports Microsoft.ConfigurationManagement.AdminConsole.Com
'Imports System.DirectoryServices.AccountManagement


Public Class SCCMSpeedup

    Public bln64BitOS As Boolean = Environment.Is64BitOperatingSystem

    'Public strRegKey As String = "Software\\SMSCollectionSpeedup"
    'Public strRegKey As String = "SOFTWARE\\Wow6432Node\\SMSCollectionSpeedup"
    'Public strRegKey As String = "SOFTWARE\\Wow6432Node\\SCCMSpeedup"
    'Public strRegKey As String = "SOFTWARE\\SCCMSpeedup"
    Public strRegKey As String  'We will determine this location later (32bit/64bit are in diff locations)

    Public dtmEvalExpireDate As Date = CDate("June 30, 2011")
    Public blnCustomerVersion As Boolean = True 'Set to True to compile Customer version
    Public blnTestMode As Boolean   ' = False   We are getting this from the Registry now
    Public strDateFormat As String

    Public METADATAAttribute As String = "msDS-ReplValueMetaData"
    Public METADATAAttribute2 As String = "msDS-ReplAttributeMetaData"
    Public SMSSERVER As String
    Public SMSSITECODE As String
    Public sSQLServerName As String
    Public sSQLServerDB As String
    Public NETBIOSDOMAINNAME As String
    Public LDAPDOMAINNAME As String
    Public ADDITIONALMINUTES As Integer
    Public SECONDSBETWEENLOOPS As Integer
    Public MINUTESBACKFORNEWSMSCLIENTS As Integer
    Public ENTRIESTORETURNPERQUERY As Integer
    Public intDebugLevel As Integer
    Public sLogPath As String
    Public sLogFileName As String
    Public LogFileFullPath As String
    Public DDRFOLDER As String
    Public GROUPEXCEPTIONSFILE As String
    Public TIMEDIFFFILE As String
    Public DDMBOXFOLDER As String
    Dim aTimer As New System.Timers.Timer
    Public oLogFile As StreamWriter
    Dim blnTimerEventRunning As Boolean = False
    Const ADDPROP_NONE As SMSRSGENCTLLib.DDRPropertyFlagsEnum = DDRPropertyFlagsEnum.ADDPROP_NONE
    Const ADDPROP_GUID As SMSRSGENCTLLib.DDRPropertyFlagsEnum = DDRPropertyFlagsEnum.ADDPROP_GUID
    Const ADDPROP_KEY As SMSRSGENCTLLib.DDRPropertyFlagsEnum = DDRPropertyFlagsEnum.ADDPROP_KEY
    Const ADDPROP_ARRAY As SMSRSGENCTLLib.DDRPropertyFlagsEnum = DDRPropertyFlagsEnum.ADDPROP_ARRAY

    'Create DDR creation object-just do this once (move this to function?)
    Dim oDDR As New SMSResGen



    Protected Overrides Sub OnStart(ByVal args() As String)
        ' Add code here to start your service. This method should set things
        ' in motion so your service can do its work.
        'StartApp()
        AddHandler aTimer.Elapsed, AddressOf OnTimedEvent
        aTimer.Interval = 1000
        aTimer.Enabled = True
        'AddHandler Me.Timer1.Elapsed, AddressOf StartApp
        'AddHandler aTimer.Disposed, AddressOf OnTimedEvent

    End Sub

    Protected Overrides Sub OnStop()
        ' Add code here to perform any tear-down necessary to stop your service.
        aTimer.Enabled = False
    End Sub



    '##############################MAIN FUNCTION##############################
    Public Function OnTimedEvent(ByVal source As Object, ByVal e As ElapsedEventArgs) As Boolean


        'Make sure the previous loop isn't still running
        Try
            aTimer.Interval = 180000    'Set the default to 3 minutes-will get changed when we pull from the registry
        Catch ex As Exception
            ErrorHandler("ERROR Setting Initial Loop interval:", Err, ex)
        End Try


        If blnTimerEventRunning = False Then
            Try
                '##Declare the variables just used in the loop##
                Dim CollectionsToUpdate As New List(Of String)
                Dim GroupToUpdateCollectionFor As New Dictionary(Of String, Nullable)
                Dim OUToUpdateCollectionFor As New Dictionary(Of String, Nullable)
                Dim TimeSinceLastRun As Integer
                Dim dtmLDAPFromDate As Date
                Dim sLDAPFromDate As String = ""
                Dim intTZOffset As Integer
                Dim intTimeDiffToCheck As Integer
                Dim dtmSMSSQLRecordTimeStamp As Date
                Dim ComputersToProcess As New Dictionary(Of String, Nullable)   'Dict for all computers to process (changed groups or new SMS client)

                Dim dtmLoopStartTime As Date
                Dim GroupsToCollections As New System.Collections.Specialized.NameValueCollection
                Dim OUsToCollections As New System.Collections.Specialized.NameValueCollection
                Dim GroupExceptionsList As New List(Of String)
                Dim ComputersThatChangedGroupsRecently As New Dictionary(Of String, Nullable)   'Use Dict to keep dupes out
                Dim ComputersThatChangedADRecently As New Dictionary(Of String, Nullable)   'Use Dict to keep dupes out
                Dim DictGroupsModifiedRecently As New Dictionary(Of String, String)


                blnTimerEventRunning = True
                'WriteToEventLog("Starting OnTimedEvent (test event log write):" & vbCrLf & "LogInfo" & vbCrLf & _
                '  "LogDetails", EventLogEntryType.Information, 1000)


                If bln64BitOS = True Then
                    strRegKey = "SOFTWARE\\Wow6432Node\\SCCMSpeedup"
                Else
                    strRegKey = "SOFTWARE\\SCCMSpeedup"
                End If


                '##GET SETTINGS FROM REG##
                dtmLoopStartTime = Now                                          'Time this loop started
                Dim blnGetSettingsFromReg As Boolean
                Try
                    blnGetSettingsFromReg = GetSettingsFromReg()
                Catch ex2 As Exception
                    ErrorHandler("ERROR GetSettingsFromReg:", Err, ex2)
                End Try

                '##OPEN LOGFILE##
                Try
                    oLogFile = File.AppendText(LogFileFullPath)
                    '    'oLogFile.AutoFlush = True
                Catch ex2 As Exception
                    '    'Don't report this error? We won't get a logfile if it fails
                    ErrorHandler("ERROR OpenLogfile:", Err, ex2)
                    OnTimedEvent = False
                    Exit Function
                End Try


                '****************************EVAL COPY CODE*******************************
                Dim blnEvalExpired As Boolean = EvalExpired()
                If blnEvalExpired = True Then
                    WriteLogEntry("WARNING: This copy of SCCMSpeedup has expired (as of " & _
                        CStr(dtmEvalExpireDate) & ")" & vbCrLf & vbTab & vbTab & vbTab & _
                        "Please contact SMSUtils for a new evaluation.", 0)
                    aTimer.Enabled = False
                    OnTimedEvent = False
                    Exit Function
                Else
                    Select Case blnCustomerVersion
                        Case True
                            WriteLogEntry("Running LICENSED version of SCCMSpeedup", 0)
                        Case False
                            WriteLogEntry("Running EVALUATION version of SCCMSpeedup - Expires " & CStr(dtmEvalExpireDate), 0)
                    End Select
                End If
                '****************************EVAL COPY CODE*******************************


                '##SET TIMER INTERVAL BASED ON REG SETTINGS##
                Try
                    If blnGetSettingsFromReg = True Then
                        WriteLogEntry("Got settings out of registry successfully", 5)
                        aTimer.Interval = SECONDSBETWEENLOOPS * 1000
                    Else
                        WriteToEventLog("ERROR Setting Loop Timer with settings from Registry", EventLogEntryType.Error, 2000)
                        'Me.Timer1.Enabled = False
                        OnTimedEvent = False
                        Exit Function
                    End If
                Catch ex2 As Exception
                    ErrorHandler("ERROR GetRegAndSetLoopTimer:", Err, ex2)
                End Try

                'Get SQL Server Name and DB name since they could be different than the SMS Server/SiteCode
                sSQLServerName = GetSMSSQLServerViaWMI()
                sSQLServerDB = GetSMSSQLDBViaWMI()


                '##Write registry values to logfile##
                Try
                    WriteLogEntry("--------------------------------------", 1)
                    WriteLogEntry("---------------STARTING---------------", 1)
                    WriteLogEntry("--------------------------------------", 1)
                    WriteLogEntry("SMSSERVER:" & vbTab & SMSSERVER, 5)
                    WriteLogEntry("SMSSITECODE:" & vbTab & SMSSITECODE, 5)
                    WriteLogEntry("SMSSQLSERVER:" & vbTab & sSQLServerName, 5)
                    WriteLogEntry("SMSSQLDB:" & vbTab & sSQLServerDB, 5)
                    WriteLogEntry("NETBIOSDOMAINNAME:" & vbTab & NETBIOSDOMAINNAME, 5)
                    WriteLogEntry("LDAPDOMAINNAME:" & vbTab & LDAPDOMAINNAME, 5)
                    WriteLogEntry("ADDITIONALMINUTES:" & vbTab & ADDITIONALMINUTES, 5)
                    WriteLogEntry("SECONDSBETWEENLOOPS:" & vbTab & SECONDSBETWEENLOOPS, 5)
                    WriteLogEntry("MINUTESBACKFORNEWSMSCLIENTS:" & vbTab & MINUTESBACKFORNEWSMSCLIENTS, 5)
                    WriteLogEntry("ENTRIESTORETURNPERQUERY:" & vbTab & ENTRIESTORETURNPERQUERY, 5)
                    WriteLogEntry("DDRFOLDER:" & vbTab & DDRFOLDER, 5)
                    WriteLogEntry("DDMBOXFOLDER:" & vbTab & DDMBOXFOLDER, 5)
                    WriteLogEntry("intDebugLevel:" & vbTab & intDebugLevel, 5)
                    'WriteLogEntry("sLogPath:" & vbTab & sLogPath, 5)
                    'WriteLogEntry("sLogFileName:" & vbTab & sLogFileName, 5)
                    WriteLogEntry("LogFileFullPath:" & vbTab & LogFileFullPath, 5)
                    WriteLogEntry("GROUPEXCEPTIONSFILE:" & vbTab & GROUPEXCEPTIONSFILE, 5)
                    WriteLogEntry("TIMEDIFFFILE:" & vbTab & TIMEDIFFFILE, 5)
                    WriteLogEntry("TESTMODE: " & vbTab & blnTestMode, 5)

                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEvent2WriteRegInfoToLog:", Err, ex2)
                End Try


                '##GET LAST TIME RAN AND SET VARIABLES BASED ON THAT##
                Try

                    TimeSinceLastRun = GetTimeSinceLastRun()                     'Number of minutes ago that the app last ran
                    WriteLogEntry("TimeSinceLastRun from Text File:" & vbTab & TimeSinceLastRun.ToString & " Minutes", 1)
                    intTimeDiffToCheck = TimeSinceLastRun + ADDITIONALMINUTES    'Number of minutes before "now" to check

                    intTZOffset = GetTZOffset()                                  'Timezone offset in minutes
                    WriteLogEntry("intTZOffset:" & vbTab & intTZOffset.ToString, 1)

                    If GetTZOffset() = 99999999 Then
                        '##Something is wrong-we cannot continue if we cannot do accurate date/time##
                        OnTimedEvent = False
                        Exit Function
                    End If

                    '##Get LDAP-formatted DateTime for LDAP queries##
                    dtmLDAPFromDate = Now.AddMinutes(intTZOffset - intTimeDiffToCheck)
                    sLDAPFromDate = dtmLDAPFromDate.ToString("yyyyMMddHHmmss.fZ") 'Date String for LDAP queries
                    WriteLogEntry("dtmLDAPFromDate:" & dtmLDAPFromDate, 1)
                    WriteLogEntry("sLDAPFromDate:" & vbTab & sLDAPFromDate, 1)

                    '##Get SQL-formatted DateTime for SQL query to find new SMS clients##
                    dtmSMSSQLRecordTimeStamp = Now.AddMinutes((-1 * (MINUTESBACKFORNEWSMSCLIENTS + TimeSinceLastRun)))
                    WriteLogEntry("dtmSMSSQLRecordTimeStamp:" & vbTab & dtmSMSSQLRecordTimeStamp, 1)
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventDates:", Err, ex2)
                End Try

                '##Get Collection/Group relationships from the database##
                Try
                    WriteLogEntry("***GETTING COLLECTION/GROUP RELATIONSHIPS***", 3)
                    GroupsToCollections = GetGroupCollectionRelationships()
                    For Each GroupToCollection As String In GroupsToCollections
                        WriteLogEntry(vbTab & GroupToCollection, 12)
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR GetCollection/Group Relationships:", Err, ex2)
                End Try

                '##Get Collection/OU relationships from the database##
                Try
                    WriteLogEntry("***GETTING COLLECTION/OU RELATIONSHIPS***", 3)
                    OUsToCollections = GetOUCollectionRelationships()
                    For Each OUToCollection As String In OUsToCollections
                        WriteLogEntry(vbTab & OUToCollection, 12)
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR GetCollection/OU Relationships:", Err, ex2)
                End Try

                '##Get GroupExceptions from text file##
                Try
                    GroupExceptionsList = GetGroupExceptions()                  'Get Group Exceptions from the text file
                    WriteLogEntry("***GETTING GROUPEXCEPTIONS***", 3)
                    For Each sGroupException As String In GroupExceptionsList
                        WriteLogEntry(vbTab & sGroupException, 12)
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventGetGroupExceptions:", Err, ex2)
                End Try

                '##Find recent group membership changes (just uses whenChanged)##
                Try
                    WriteLogEntry("***GETTING GROUPS THAT CHANGED RECENTLY***", 3)
                    DictGroupsModifiedRecently = FindGroupsModifiedRecently(sLDAPFromDate, intTZOffset, GroupExceptionsList, GroupsToCollections)
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventGetGroupChanges:", Err, ex2)
                End Try

                '##Process each group that changed recently to find the specific computers that changed##
                Try
                    WriteLogEntry("***GETTING COMPUTERS THAT CHANGED RECENTLY***", 3)

                    'Find members that changed recently
                    'Add groups that changed recently to GroupToUpdateCollectionFor 
                    'TODO get keys so we can log short group names (get values via the keys for calling the function)
                    For Each sGroupDN As String In DictGroupsModifiedRecently.Values

                        'Get membership changes for each group and add unique entries to ComputersThatChangedGroupsRecently
                        Dim MemberChangesForGroup As New List(Of String)
                        MemberChangesForGroup = GetRecentChangesForGroupNew(sGroupDN, intTimeDiffToCheck)
                        'Grab the current groups changes and add anything new to the 'ComputersThatChangedGroupsRecently' Dictionary
                        For Each MemberChange As String In MemberChangesForGroup
                            If ComputersThatChangedGroupsRecently.ContainsKey(MemberChange) = False Then
                                ComputersThatChangedGroupsRecently.Add(MemberChange, Nothing)
                            End If
                        Next
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventGetCompChanges:", Err, ex2)
                End Try

                '########################################################
                'New-Checks AD for recent computer changes (i.e. OU changes)##
                WriteLogEntry("***COMPUTERS WITH RECENT AD CHANGES***", 3)
                Dim RecentComputerADChanges As New Dictionary(Of String, String)
                RecentComputerADChanges = GetRecentADComputerChanges(sLDAPFromDate, intTZOffset, intTimeDiffToCheck)
                For Each ComputerADChange As String In RecentComputerADChanges.Keys
                    If ComputersThatChangedADRecently.ContainsKey(ComputerADChange) = False Then
                        ComputersThatChangedADRecently.Add(ComputerADChange, Nothing)
                    End If
                Next

                '########################################################

                '##Computers with recent AD Group changes (Add these to ComputersToProcess)##
                Try
                    WriteLogEntry("***COMPUTERS WITH RECENT AD GROUP CHANGES***", 3)
                    WriteLogEntry(vbTab & "Computers with recent AD group changes COUNT: " & ComputersThatChangedGroupsRecently.Count, 5)

                    For Each sComputer As String In ComputersThatChangedGroupsRecently.Keys
                        WriteLogEntry(vbTab & sComputer, 5)
                        If ComputersToProcess.ContainsKey(sComputer) = False Then
                            ComputersToProcess.Add(sComputer, Nothing)
                        End If
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventRecentADGroupChanges:", Err, ex2)
                End Try

                '##Computers with recent AD changes (Add these to ComputersToProcess)##
                Try
                    WriteLogEntry("***COMPUTERS WITH RECENT AD CHANGES***", 3)
                    WriteLogEntry(vbTab & "Computers with recent AD changes COUNT: " & ComputersThatChangedADRecently.Count, 5)

                    For Each sComputer As String In ComputersThatChangedADRecently.Keys
                        WriteLogEntry(vbTab & sComputer, 5)
                        If ComputersToProcess.ContainsKey(sComputer) = False Then
                            ComputersToProcess.Add(sComputer, Nothing)
                        End If
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR OnTimedEventRecentADChanges:", Err, ex2)
                End Try

                '##Find New SMS Clients (Add these to ComputersToProcess)##
                Try
                    WriteLogEntry("***NEWSMSCLIENTS***", 3)

                    Dim DictNewSMSClients As New Dictionary(Of String, String)
                    DictNewSMSClients = FindNewSMSClientRecords(dtmSMSSQLRecordTimeStamp)
                    WriteLogEntry(vbTab & "New SMS Client Count: " & DictNewSMSClients.Count, 5)
                    For Each sNewSMSClientDN As String In DictNewSMSClients.Keys
                        WriteLogEntry(vbTab & sNewSMSClientDN, 5)
                        If ComputersToProcess.ContainsKey(sNewSMSClientDN) = False Then
                            ComputersToProcess.Add(sNewSMSClientDN, Nothing)
                        End If
                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR FindNewSMSClients:", Err, ex2)
                End Try

                '##Process computers##
                Try
                    'Just logging all computers we will process before starting on them
                    WriteLogEntry("***ALLCOMPUTERSTOPROCESS***", 3)
                    WriteLogEntry(vbTab & "Computers to Process Count: " & ComputersToProcess.Count.ToString(), 5)
                    For Each sComputerToProcess As String In ComputersToProcess.Keys
                        WriteLogEntry(vbTab & sComputerToProcess, 5)
                    Next

                    For Each sComputerToProcess As String In ComputersToProcess.Keys
                        WriteLogEntry("Processing computer" & vbTab & sComputerToProcess, 3)
                        'GetGroupsForADObject(sComputerToProcess)

                        'TODO WHY ARE WE PASSING THIS AS A PARAMETER INSTEAD OF GETTING THE FUNCTION RETURN (maybe to return boolean for success/failure?)
                        Dim ComputerGroupMembers As New ArrayList
                        Dim ComputerOUs As New ArrayList
                        Call CreateADGroupDiscDDR(sComputerToProcess, ComputerGroupMembers, ComputerOUs)

                        'Add all groups that the object is a member of to GroupToUpdateCollectionFor Dict
                        For Each sGroup As String In ComputerGroupMembers
                            If GroupToUpdateCollectionFor.ContainsKey(sGroup) = False Then GroupToUpdateCollectionFor.Add(sGroup, Nothing)
                        Next

                        'Add all OUs that the object is a member of to OUToUpdateCollectionFor Dict
                        For Each sOU As String In ComputerOUs
                            If OUToUpdateCollectionFor.ContainsKey(sOU) = False Then OUToUpdateCollectionFor.Add(sOU, Nothing)
                        Next

                    Next
                Catch ex2 As Exception
                    ErrorHandler("ERROR ProcessAllComputers:", Err, ex2)
                End Try

                'Add all recently changed groups to GroupToUpdateCollectionFor Dict
                For Each sGroupName As String In DictGroupsModifiedRecently.Keys
                    'Add all recently changed groups to GroupToUpdateCollectionFor
                    If GroupToUpdateCollectionFor.ContainsKey(sGroupName) = False Then
                        GroupToUpdateCollectionFor.Add(sGroupName, Nothing)
                    End If
                Next


                'TODO DONE? Get all groups for changed computers (new sms clients or ones that changed groups)

                'Get the CollectionIDs from the database for the groups that were modified (or new sms clients are a member of)
                WriteLogEntry("***Finding CollectionIDs for groups***", 3)
                Call FindCollectionsForGroups(GroupToUpdateCollectionFor, CollectionsToUpdate, GroupsToCollections)

                WriteLogEntry("***Finding CollectionIDs for OUs***", 3)
                Call FindCollectionsForOUs(OUToUpdateCollectionFor, CollectionsToUpdate, OUsToCollections)


                WriteLogEntry("***Calling UpdateCollections2***", 3)
                Call UpdateCollections2(CollectionsToUpdate)

                Dim intLoopTimeinSecs As Integer = CInt(Now.Subtract(dtmLoopStartTime).TotalSeconds)

                'old way to loop from the form-based app
                'If intLoopTimeinSecs < SECONDSBETWEENLOOPS - 5 Then
                'intLoopWait = SECONDSBETWEENLOOPS - intLoopTimeinSecs
                'Else
                'intLoopWait = 10
                'End If

                WriteLogEntry("---------------LOOP COMPLETE IN " & intLoopTimeinSecs.ToString & " SECONDS---------------", 1)

                Try
                    WriteLogEntry("Closing logfile", 1)
                    oLogFile.Close()
                    '    'Dispose seems to cause the entire service to bomb out
                    '    'oLogFile.Dispose()
                Catch ex2 As Exception
                    ErrorHandler("ERROR Closing LogFile", Err, ex2)
                End Try

                OnTimedEvent = True

            Catch ex As Exception
                OnTimedEvent = False
                ErrorHandler("ERROR OnTimedEvent:", Err, ex)
                oLogFile.Close()
            End Try

            blnTimerEventRunning = False
        Else
            WriteToEventLog("Starting OnTimedEvent (previous thread still running-skip for now):" & vbCrLf & "LogInfo" & vbCrLf & _
                "LogDetails", EventLogEntryType.Warning, 1001)

            OnTimedEvent = True
        End If

    End Function
    Function GetSMSSQLServerViaWMI() As String

        Dim sSMSSQLServerName As String = ""
        Dim scope As New ManagementScope("\\" & SMSSERVER & "\root\sms\site_" & SMSSITECODE)

        Try
            scope.Connect()
        Catch
            Dim errorText As String = "Error connecting to SMS server via WMI." & vbCrLf & _
                "Error: " & Err.Number & vbCrLf & _
                "Description: " & Err.Description & vbCrLf & _
                "SMS Server: " & SMSSERVER & vbCrLf & _
                "SMS Site: " & SMSSITECODE
            WriteToEventLog(errorText, EventLogEntryType.Error, 2000)
            WriteLogEntry(errorText, 1)
            'aTimer.Interval = 3600000   'Set timer to 1 hour
            aTimer.Interval = 360000   'Set timer to 6 minutes
            'Me.Timer1.Enabled = False
            GetSMSSQLServerViaWMI = ""
            Exit Function
        End Try

        WriteLogEntry("SUCCESS: Connected OK to \\" & SMSSERVER, 3)
        Dim oQuery As New ObjectQuery("SELECT * FROM SMS_SCI_SiteDefinition WHERE SiteCode = '" & SMSSITECODE & "'")
        Dim oSearcher As New ManagementObjectSearcher(scope, oQuery)
        Dim colSCIComponentSet As ManagementObjectCollection
        colSCIComponentSet = oSearcher.Get
        For Each SCIComponent As ManagementObject In colSCIComponentSet
            sSMSSQLServerName = CStr(SCIComponent("SQLServerName"))
        Next
        GetSMSSQLServerViaWMI = sSMSSQLServerName
    End Function

    Function GetSMSSQLDBViaWMI() As String
        Dim sSMSSQLServerDB As String = ""
        Dim scope As New ManagementScope("\\" & SMSSERVER & "\root\sms\site_" & SMSSITECODE)
        Try
            scope.Connect()
        Catch
            Dim errorText As String = "Error connecting to SMS server via WMI." & vbCrLf & _
                "Error: " & Err.Number & vbCrLf & _
                "Description: " & Err.Description & vbCrLf & _
                "SMS Server: " & SMSSERVER & vbCrLf & _
                "SMS Site: " & SMSSITECODE
            WriteToEventLog(errorText, EventLogEntryType.Error, 2000)
            WriteLogEntry(errorText, 1)
            'aTimer.Interval = 3600000   'Set timer to 1 hour
            aTimer.Interval = 360000   'Set timer to 6 minutes
            'Me.Timer1.Enabled = False
            GetSMSSQLDBViaWMI = ""
            Exit Function
        End Try

        WriteLogEntry("SUCCESS: Connected OK to \\" & SMSSERVER, 3)
        Dim oQuery As New ObjectQuery("SELECT * FROM SMS_SCI_SiteDefinition WHERE SiteCode = '" & SMSSITECODE & "'")
        Dim oSearcher As New ManagementObjectSearcher(scope, oQuery)
        Dim colSCIComponentSet As ManagementObjectCollection
        colSCIComponentSet = oSearcher.Get
        For Each SCIComponent As ManagementObject In colSCIComponentSet
            sSMSSQLServerDB = CStr(SCIComponent("SQLDatabaseName"))
        Next
        GetSMSSQLDBViaWMI = sSMSSQLServerDB

    End Function

    Function GetRecentADComputerChanges(ByVal sLDAPFromDate As String, ByVal intTZOffset As Integer, ByVal intTimeDiffToCheck As Integer) As Dictionary(Of String, String)
        Try

            Dim RecentComputersDict As New Dictionary(Of String, String)

            'WriteLogEntry(vbTab & "Querying: " & "(&(objectCategory=computer)(whenChanged>=" & sLDAPFromDate & "))", 5)
            Dim ADRoot As New DirectoryEntry("LDAP://" & LDAPDOMAINNAME)
            Dim ADSearcher As New DirectorySearcher(ADRoot)
            ADSearcher.Filter = "(&(objectCategory=computer)(whenChanged>=" & sLDAPFromDate & "))"


            ADSearcher.PropertiesToLoad.Add("distinguishedName")
            ADSearcher.PropertiesToLoad.Add("Name")
            ADSearcher.PropertiesToLoad.Add("whenChanged")
            Dim SearchResult As System.DirectoryServices.SearchResultCollection = ADSearcher.FindAll()
            'MsgBox("LDAP COUNT: " & colSearchResult.Count)
            'Debug.Print("LDAP COUNT: " & SearchResult.Count)
            WriteLogEntry(vbTab & "Number of Recent Computer Changes:" & vbTab & SearchResult.Count, 1)
            For Each oComputer As SearchResult In SearchResult
                Dim sObjectDN As String = oComputer.Properties("distinguishedName")(0).ToString()
                Dim sObjectName As String = oComputer.Properties("Name")(0).ToString()
                Dim sObjectWhenChangedUTC As Date = CDate(oComputer.Properties("whenChanged")(0))
                Dim sObjectWhenChangedLocal As Date = sObjectWhenChangedUTC.AddMinutes(intTZOffset * -1)
                'MsgBox(sObjectDN & vbCrLf & sObjectName & vbCrLf & sObjectWhenChangedUTC & vbCrLf & sObjectWhenChangedLocal)
                'WriteLogEntry(vbTab & vbTab & sObjectName & vbTab & sObjectDN, 5)

                Dim ADComputer As New DirectoryEntry("LDAP://" & sObjectDN)
                ADComputer.RefreshCache(New String() {METADATAAttribute2})
                'Dim sGroupName As String = ADGroup.Properties("Name").Value.ToString
                Dim PropColl As PropertyValueCollection = ADComputer.Properties(METADATAAttribute2)
                'WriteLogEntry(PropColl.ToString, 5)
                For Each ReplAttributeMetaDataEntry As String In PropColl
                    'WriteLogEntry("#####MEMNAME " & ReplValueMetaDataEntry, 1)
                    'IntNumRetrieved += 1

                    'values.Add(propValue)
                    'MemberCount2 += 1
                    '********************************
                    '********************************
                    'TODO CHECK IF EXIST AND EXIT LOOP
                    'gets stuck in a loop if the range interval matches the number of members
                    '********************************
                    '********************************
                    'WriteLogEntry(vbTab & vbTab & vbTab & "replattribute", 5)
                    Try

                        Dim XMLDoc As New Xml.XmlDocument
                        XMLDoc.LoadXml(ReplAttributeMetaDataEntry)
                        Dim sAttributeName As String = XMLDoc.DocumentElement.SelectSingleNode("pszAttributeName").InnerText
                        'WriteLogEntry(vbTab & vbTab & vbTab & sAttributeName, 5)
                        If sAttributeName = "name" Then

                            Dim sLastOrigChange As String = XMLDoc.DocumentElement.SelectSingleNode("ftimeLastOriginatingChange").InnerText
                            Dim dtmLastOrigChange As Date = CDate(sLastOrigChange)
                            'WriteLogEntry(vbTab & vbTab & vbTab & sObjectName & vbTab & sAttributeName & vbTab & dtmLastOrigChange, 5)
                            'Dim sMemberDN As String = XMLDoc.DocumentElement.SelectSingleNode("pszObjectDn").InnerText
                            Dim TimeDiff As TimeSpan = Now.Subtract(dtmLastOrigChange)
                            If TimeDiff.TotalMinutes <= intTimeDiffToCheck Then
                                'If GetObjectClass(sMemberDN) = "computer" Then
                                WriteLogEntry(vbTab & vbTab & "ComputerADChangeToProcess:" & vbTab & sObjectDN, 5)
                                RecentComputersDict.Add(sObjectDN, sObjectName)
                                'End If
                            Else
                                'WriteLogEntry(vbTab & vbTab & vbTab & "COMPUTERNOTRECENTLYCHANGEDINAD:" & vbTab & sObjectDN, 5)
                            End If

                        Else
                            'WriteLogEntry(vbTab & vbTab & vbTab & sAttributeName, 5)
                        End If

                    Catch ex2 As Exception
                        'WriteLogEntry("ERRORGETGROUPPROPS2: " & sGroupDN, 1)
                        ErrorHandler("ERROR GetRecentADComputerChanges:", Err, ex2)
                    End Try

                Next

            Next
            GetRecentADComputerChanges = RecentComputersDict
        Catch ex As Exception
            ErrorHandler("ERROR GetRecentADComputerChanges:", Err, ex)
            GetRecentADComputerChanges = New Dictionary(Of String, String)
        End Try

    End Function
    Function GetOUCollectionRelationships() As System.Collections.Specialized.NameValueCollection



        Dim SQLConn As New SqlConnection()
        Dim SQLCmd As New SqlCommand()

        Try

            Dim OUsToCollections As New System.Collections.Specialized.NameValueCollection

            Dim sConnString As String = "Data Source=" & sSQLServerName & ";Initial Catalog=" & sSQLServerDB & ";Integrated Security=True"
            'Dim sSQLQuery As String = "SELECT TOP 5 * FROM v_R_System"
            'Dim sSQLQuery As String = "SELECT TOP 2 * FROM v_R_System WHERE Creation_Date0 > '2009-10-28 14:00' ORDER BY Name0 DESC"
            Dim sSQLQuery As String = "SELECT * FROM v_CollectionRuleQuery where QueryExpression like '%System_OU_Name0%'"
            'Dim SQLConn As New SqlConnection()
            'Dim SQLCmd As New SqlCommand()
            SQLConn.ConnectionString = sConnString
            SQLConn.Open()
            SQLCmd.Connection = SQLConn
            SQLCmd.CommandText = sSQLQuery
            Dim SQLReader As SqlClient.SqlDataReader = SQLCmd.ExecuteReader
            While SQLReader.Read
                Dim sCollectionID As String = SQLReader("CollectionID").ToString
                'MsgBox(sShortName)
                'Dim sObjectDN As String
                Dim sQueryExpression As String = SQLReader("QueryExpression").ToString
                'WriteLogEntry(sCollectionID & vbTab & sQueryExpression, 1)
                Dim OUNames As List(Of String) = RegExpTester("OU", sQueryExpression)
                For Each OUName As String In OUNames
                    OUsToCollections.Add(OUName, sCollectionID)
                    WriteLogEntry(vbTab & "OU: " & sCollectionID & vbTab & OUName, 15)
                Next
            End While
            GetOUCollectionRelationships = OUsToCollections
        Catch ex As Exception
            ErrorHandler("ERROR GetGroupCollectionRelationships:", Err, ex)
            GetOUCollectionRelationships = New System.Collections.Specialized.NameValueCollection
        Finally
            SQLConn.Close()
            SQLConn.Dispose()
        End Try

    End Function

    Function GetGroupCollectionRelationships() As System.Collections.Specialized.NameValueCollection

        Dim SQLConn As New SqlConnection()
        Dim SQLCmd As New SqlCommand()

        Try

            Dim GroupsToCollections As New System.Collections.Specialized.NameValueCollection

            Dim sConnString As String = "Data Source=" & sSQLServerName & ";Initial Catalog=" & sSQLServerDB & ";Integrated Security=True"
            'Dim sSQLQuery As String = "SELECT TOP 5 * FROM v_R_System"
            'Dim sSQLQuery As String = "SELECT TOP 2 * FROM v_R_System WHERE Creation_Date0 > '2009-10-28 14:00' ORDER BY Name0 DESC"
            Dim sSQLQuery As String = "SELECT * FROM v_CollectionRuleQuery where QueryExpression like '%System_Group_Name0%'"
            'Dim SQLConn As New SqlConnection()
            'Dim SQLCmd As New SqlCommand()
            SQLConn.ConnectionString = sConnString
            SQLConn.Open()
            SQLCmd.Connection = SQLConn
            SQLCmd.CommandText = sSQLQuery
            Dim SQLReader As SqlClient.SqlDataReader = SQLCmd.ExecuteReader
            While SQLReader.Read
                Dim sCollectionID As String = SQLReader("CollectionID").ToString
                'MsgBox(sShortName)
                'Dim sObjectDN As String
                Dim sQueryExpression As String = SQLReader("QueryExpression").ToString
                'WriteLogEntry(sCollectionID & vbTab & sQueryExpression, 1)
                Dim GroupNames As List(Of String) = RegExpTester("GROUP", sQueryExpression)
                For Each GroupName As String In GroupNames
                    GroupsToCollections.Add(GroupName, sCollectionID)
                    WriteLogEntry(vbTab & "GROUP: " & sCollectionID & vbTab & GroupName, 15)
                Next
            End While
            GetGroupCollectionRelationships = GroupsToCollections
        Catch ex As Exception
            ErrorHandler("ERROR GetGroupCollectionRelationships:", Err, ex)
            GetGroupCollectionRelationships = New System.Collections.Specialized.NameValueCollection
        Finally
            SQLConn.Close()
            SQLConn.Dispose()
        End Try

    End Function

    Function RegExpTester(ByVal TypeOfCheck As String, ByVal StringToCheck As String) As List(Of String)

        Try

            'WriteLogEntry(StringToCheck, 1)

            Dim Matches As New List(Of String)
            'where __stem_System_Group_Name_ARR0.System_Group_Name0 = 'DS\G001-LotusNotes65'
            'where __mSystem_System_OU_Name_ARR0.System_OU_Name0 = 'DS.AD.SSMHC.COM/SSMHC/SSMSTL/IC'

            'TODO can return more than one, just add to dict here and return back to main function? (dim dict in main function)

            'Dim RegExpPattern As String = "System_Group_Name0 (=|!=) ['" & Chr(34) & "]DS\\.*?['" & Chr(34) & "]"   'works for single or double quote"
            'Dim RegExpPattern As String = "System_Group_Name0 (=|!=) ['" & Chr(34) & "]" & NETBIOSDOMAINNAME & "\\.*?['" & Chr(34) & "]"   'works for single or double quote"
            Dim RegExpPattern As String = ""
            Select Case TypeOfCheck
                Case "GROUP"
                    RegExpPattern = "System_Group_Name0 (=|!=) ['" & Chr(34) & "]" & NETBIOSDOMAINNAME & "\\.*?['" & Chr(34) & "]"   'works for single or double quote"
                Case "OU"
                    RegExpPattern = "System_OU_Name0 (=|!=) ['" & Chr(34) & "].*?['" & Chr(34) & "]"
            End Select
            'NETBIOSDOMAINNAME
            'Dim RegExpPattern As String = "System_Group_Name0"
            Dim myMatches As RegularExpressions.MatchCollection
            'Dim myRegex As New RegularExpressions.Regex("\w+")
            Dim myRegex As New RegularExpressions.Regex(RegExpPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase)
            'myRegex.Options.IgnoreCase()

            myMatches = myRegex.Matches(StringToCheck)
            For Each successfulMatch As RegularExpressions.Match In myMatches
                Dim MatchValue As String = successfulMatch.Value
                Dim MatchValueWithoutQuotes As String
                'WriteLogEntry(vbTab & "MatchValue: " & MatchValue, 1)
                'WriteLogEntry(vbTab & successfulMatch.Index.ToString, 1)
                'TODO Make this a select-case?
                MatchValueWithoutQuotes = Replace(MatchValue, "System_Group_Name0 = ", "", 1, -1, CompareMethod.Text)
                MatchValueWithoutQuotes = Replace(MatchValueWithoutQuotes, "System_OU_Name0 = ", "", 1, -1, CompareMethod.Text)
                'WriteLogEntry("**1**" & vbTab & GroupName, 10)
                'WriteLogEntry(vbTab & GroupName, 1)
                'GroupName = Replace(GroupName, "System_Group_Name0 != ", "", 1, -1, CompareMethod.Text)
                MatchValueWithoutQuotes = Replace(MatchValueWithoutQuotes, Chr(34), "", 1, -1, CompareMethod.Text)
                'WriteLogEntry("**2**" & vbTab & GroupName, 10)
                MatchValueWithoutQuotes = Replace(MatchValueWithoutQuotes, Chr(39), "", 1, -1, CompareMethod.Text)
                'WriteLogEntry("**3**" & vbTab & GroupName, 10)
                Matches.Add(MatchValueWithoutQuotes)
            Next
            RegExpTester = Matches
        Catch ex As Exception
            ErrorHandler("ERROR RegExpTester: " & StringToCheck, Err, ex)
            RegExpTester = New List(Of String)
        End Try

    End Function

    ''Function GetInfoFromDataBase() As Dictionary(Of String, String)
    'Function GetInfoFromDataBase() As System.Collections.Specialized.NameValueCollection
    '    'Not used anymore since we can do a direct query against the SCCM SQL DB each loop quickly

    '    Try

    '        Dim QueryString As String = "Select * from SMSCollectionToADGroup"
    '        'Debug.Print(QueryString)
    '        'WriteLogEntry(vbTab & vbTab & QueryString, 1)
    '        'Dim ConnString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\\ds.ad.ssmhc.com\ssmdfs\IC\UserHome\mbroad\My Documents\Prog\PowerShell\SMSCollectionToADGroup.mdb"
    '        Dim ConnString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & COLLECTIONTOGROUPDATABASE
    '        Dim Conn As New OleDb.OleDbConnection(ConnString)
    '        Dim Cmd As New OleDb.OleDbCommand(QueryString, Conn)

    '        'Dim myDataAdapter As New OleDb.OleDbDataAdapter(QueryString, Conn)
    '        'Dim myDataSet As New DataSet("SMSCollectionToADGroup")
    '        'myDataAdapter.Fill(myDataSet, "SMSCollectionToADGroup")
    '        'Debug.Print("getting db info")

    '        'myDataSet.

    '        '//pass the Select statement and connection information to the initializxation constructor for the OleDBDataAdapter
    '        'System.Data.OleDb.OleDbDataAdapter myDataAdapter = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM BOOKS",conn);

    '        '//Create a new dataset with a table : Books
    '        'System.Data.DataSet myDataSet = new System.Data.DataSet("Books");

    '        '//Fill the dataset and table with the data retrieved by the select command
    '        'myDataAdapter.Fill(myDataSet,"Books");

    '        '//return the new dataset created 
    '        'return myDataSet;

    '        Dim GroupsToCollections As New System.Collections.Specialized.NameValueCollection


    '        Dim DR As OleDb.OleDbDataReader
    '        Conn.Open()
    '        'DR = Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection)
    '        DR = Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection)
    '        'debug.Print dr.
    '        While DR.Read()
    '            Dim sCollectionID As String = DR("CollectionID").ToString
    '            Dim sGroupName As String = DR("Groupname").ToString
    '            'Debug.Print("*** " & sCollectionID)
    '            'WriteLogEntry(vbTab & vbTab & sCollectionID, 1)
    '            GroupsToCollections.Add(sGroupName, sCollectionID)
    '            'Add CollectionID to GroupToUpdateCollectionFor Dict
    '            'If GroupToUpdateCollectionFor.ContainsKey(sCollectionID) = False Then GroupToUpdateCollectionFor.Add(sCollectionID, Nothing)
    '            'CollectionsToUpdate.Add(sCollectionID)

    '        End While
    '        DR.Close() 'IMPORTANT OR IT WILL LOCK THE DB

    '        'For Each Value As String In GroupsToCollections.GetValues("DS\G001-DenyAutoPatch - Computer")
    '        '    Debug.Print(Value)
    '        'Next
    '        'Values = markStatus.GetValues(key)
    '        'For Each value As String In Values
    '        '    MsgBox(key & " - " & value)
    '        'Next value
    '        GetInfoFromDataBase = GroupsToCollections

    '    Catch ex As Exception

    '    End Try

    'End Function

    Function FindCollectionsForGroups(ByVal GroupToUpdateCollectionFor As Dictionary(Of String, Nullable), _
        ByRef CollectionsToUpdate As List(Of String), _
        ByVal GroupsToCollections As System.Collections.Specialized.NameValueCollection) As Boolean
        'Uses the NameValueCollection that is stored in memory to find all CollectionIDs for a each group

        Try

            'WriteLogEntry("Finding CollectionIDs for groups", 1)
            For Each sGroupToUpdateCollectionFor As String In GroupToUpdateCollectionFor.Keys   'Go through each group...
                'WriteLogEntry(vbTab & "sGroupToUpdateCollectionFor: " & sGroupToUpdateCollectionFor, 1)

                Try

                    Dim CollectionIDs As String()
                    'CollectionIDs = GroupsToCollections.GetValues(NETBIOSDOMAINNAME & "\" + sGroupToUpdateCollectionFor)
                    CollectionIDs = GroupsToCollections.GetValues(sGroupToUpdateCollectionFor)

                    If (CollectionIDs Is Nothing) = False Then    'For/Next errors out if nothing is returned and we don't check for this first
                        For Each CollectionID As String In CollectionIDs
                            'Debug.Print(CollectionID)
                            WriteLogEntry(vbTab & CollectionID & vbTab & sGroupToUpdateCollectionFor, 5)
                            CollectionsToUpdate.Add(CollectionID)
                            'FindCollectionsForGroups = True
                        Next
                        'FindCollectionsForGroups = True
                    Else
                        WriteLogEntry(vbTab & vbTab & vbTab & sGroupToUpdateCollectionFor, 7)
                        'FindCollectionsForGroups = True
                    End If
                    'TODO Return FindCollectionsForGroups properly
                    'FindCollectionsForGroups = True
                Catch ex2 As Exception
                    ErrorHandler("ERROR FindCollectionsForGroups: " & sGroupToUpdateCollectionFor, Err, ex2)
                End Try

            Next
            FindCollectionsForGroups = True
        Catch ex As Exception
            ErrorHandler("ERROR FindCollectionsForGroupsMain", Err, ex)
            FindCollectionsForGroups = False
        End Try

    End Function
    Function FindCollectionsForOUs(ByVal OUToUpdateCollectionFor As Dictionary(Of String, Nullable), _
        ByRef CollectionsToUpdate As List(Of String), _
        ByVal OUsToCollections As System.Collections.Specialized.NameValueCollection) As Boolean
        'Uses the NameValueCollection that is stored in memory to find all CollectionIDs for a each OU

        Try

            'WriteLogEntry("Finding CollectionIDs for OUs", 1)
            For Each sOUToUpdateCollectionFor As String In OUToUpdateCollectionFor.Keys   'Go through each OU...
                'WriteLogEntry(vbTab & "sOUToUpdateCollectionFor: " & sOUToUpdateCollectionFor, 1)

                Try

                    Dim CollectionIDs As String()
                    'CollectionIDs = GroupsToCollections.GetValues(NETBIOSDOMAINNAME & "\" + sGroupToUpdateCollectionFor)
                    CollectionIDs = OUsToCollections.GetValues(sOUToUpdateCollectionFor)

                    If (CollectionIDs Is Nothing) = False Then    'For/Next errors out if nothing is returned and we don't check for this first
                        For Each CollectionID As String In CollectionIDs
                            'Debug.Print(CollectionID)
                            WriteLogEntry(vbTab & CollectionID & vbTab & sOUToUpdateCollectionFor, 5)
                            CollectionsToUpdate.Add(CollectionID)
                            'FindCollectionsForGroups = True
                        Next
                        'FindCollectionsForGroups = True
                    Else
                        WriteLogEntry(vbTab & vbTab & vbTab & sOUToUpdateCollectionFor, 7)
                        'FindCollectionsForGroups = True
                    End If
                    'TODO Return FindCollectionsForGroups properly
                    'FindCollectionsForGroups = True
                Catch ex2 As Exception
                    ErrorHandler("ERROR FindCollectionsForOUs: " & sOUToUpdateCollectionFor, Err, ex2)
                End Try

            Next
            FindCollectionsForOUs = True
        Catch ex As Exception
            ErrorHandler("ERROR FindCollectionsForOUsMain", Err, ex)
            FindCollectionsForOUs = False
        End Try

    End Function


    Function UpdateCollections2(ByVal CollectionsToUpdate As List(Of String)) As Boolean

        Try

            CollectionsToUpdate.Sort()

            'TODO move to function and return connect object
            'Objects for binding to the SMS server
            Dim NamedValues As New SmsNamedValuesDictionary()
            Dim Connection As New WqlConnectionManager(NamedValues)
            Try

                If (System.Net.Dns.GetHostName().ToUpper() = SMSSERVER.ToUpper()) Then
                    'Connect to local computer.
                    Connection.Connect(SMSSERVER)
                Else
                    'Connect to remote computer.
                    Connection.Connect(SMSSERVER)
                    'connection.Connect(SMSSERVER, userName, userPassword)
                End If
                'return connection;
            Catch ex2 As Exception
                ErrorHandler("ERROR UpdateCollection2-Connect", Err, ex2)

            End Try

            WriteLogEntry("***Refreshing Collections***", 3)
            Try

                For Each sCollectionID As String In CollectionsToUpdate
                    'Debug.Print(sCollectionID)
                    Dim Colls As IResultObject = Connection.QueryProcessor.ExecuteQuery("Select * from SMS_Collection where CollectionID='" & sCollectionID & "'")
                    For Each Coll As IResultObject In Colls
                        Coll.Get()
                        Dim CollName As String = Coll.Properties("Name").StringValue

                        WriteLogEntry(vbTab & sCollectionID & vbTab & CollName, 5)
                        If blnTestMode = False Then

                            Try

                                'Refresh Collection
                                Dim RequestRefreshParameters As New Dictionary(Of String, Object)
                                RequestRefreshParameters.Add("IncludeSubCollections", False)
                                Coll.ExecuteMethod("RequestRefresh", RequestRefreshParameters)
                            Catch ex2 As Exception
                                ErrorHandler("ERROR UpdateCollections2-2: " & CollName, Err, ex2)
                                UpdateCollections2 = False
                            End Try
                        Else
                            WriteLogEntry(vbTab & vbTab & "TESTMODE-Not refreshing", 5)
                        End If

                    Next
                Next
                UpdateCollections2 = True

            Catch ex2 As Exception
                ErrorHandler("ERROR UpdateCollections2-1:", Err, ex2)
                UpdateCollections2 = False
            End Try

            Connection.Dispose()
        Catch ex As Exception
            UpdateCollections2 = False
            ErrorHandler("ERROR UpdateCollections2-3", Err, ex)
        End Try

    End Function




    Function GetGroupsForADObjectold(ByVal CompDN As String) As Array
        'Returns group members of an object as an Array (must be Array for DDR)
        'This is faster than the methods built-in to .Net4

        Dim GroupsForADObject As New ArrayList
        Try

            Dim ADObject As DirectoryEntry = New DirectoryEntry("LDAP://" & CompDN)
            ADObject.RefreshCache(New String() {"TokenGroups"})

            Dim SecIdentRefColl As New System.Security.Principal.IdentityReferenceCollection
            Dim intGroupsForObject As Integer = ADObject.Properties("TokenGroups").Count
            WriteLogEntry(vbTab & "Number of groups for object: " & intGroupsForObject.ToString, 3)
            'WriteLogEntry(ADObject.Properties("TokenGroups").Count.ToString
            'For Each SidByte As System.Security.Principal.IdentityReference In ADObject.Properties("TokenGroups")
            For Each SidByte() As System.Byte In ADObject.Properties("TokenGroups")
                'Debug.Print("BYTE: " & SidByte)
                'Dim SidByte2 As System.Security.Principal.SecurityIdentifier = DirectCast(SidByte, System.Security.Principal.SecurityIdentifier)
                'SecIdentRefColl.Add((SidByte2))
                Dim octetSid As String = ConvertToOctetString(SidByte)
                'Debug.Print(octetSid)
                Dim groupPath As String = String.Format("<SID={0}>", octetSid)

                'you will need to add your correct path and credentials to the constructor here, but you get the idea...
                Dim groupEntry As New DirectoryEntry("LDAP://" & groupPath)
                'do something like this with the group once you have bound it
                Dim sGroupName As String = groupEntry.Properties("samAccountName").Value.ToString()
                GroupsForADObject.Add(NETBIOSDOMAINNAME & "\" & sGroupName)
                'Debug.Print(sGroupName)
                WriteLogEntry(vbTab & vbTab & sGroupName, 7)

            Next

            GetGroupsForADObjectold = GroupsForADObject.ToArray
            'For Each IdentRef As System.Security.Principal.SecurityIdentifier In SecIdentRefColl.Translate(Type.GetType("System.Security.Principal.NTAccount"))
            'Debug.Print("GROUP: " & ADObject.Properties("SamAccountName").ToString & vbTab & IdentRef.Value)
            'Next

            ''Dim sids As System.Security.Principal.IdentityReferenceCollection = _
            ''    DirectCast(ADObject.Identity, System.Security.Principal.WindowsIdentity).Groups
            ''Dim names As System.Security.Principal.IdentityReferenceCollection = _
            ''    sids.Translate(GetType(System.Security.Principal.NTAccount))
            ''For Each name As System.Security.Principal.NTAccount In names
            ''    Debug.Print(name.ToString())
            ''Next

            '$ADObject.psbase.refreshCache(@("TokenGroups"))
            '$secirc = new-object System.Security.Principal.IdentityReferenceCollection
            'foreach($sidByte in $ADObject.TokenGroups)
            '	{$secirc.Add((New-Object System.Security.Principal.SecurityIdentifier $sidByte,0))}
            'foreach($grp in $secirc.Translate([System.Security.Principal.NTAccount]))
            '	{$arrGroups+=$grp.Value}


            'List<string> groups = new List<string>();
            '  IdentityReferenceCollection irc = id.Groups.Translate(typeof(NTAccount));


            '  foreach (NTAccount acc in irc)
            '  {
            '    groups.Add(acc.Value);
            '  }
            '  return groups.ToArray();



            '##Get Groups array##
            '[array]$arrGroups = @()
            '#$ADObject|gm
            '#Write-Host("GETGROUPS: " + $ADObject.CN)
            '$ADObject.psbase.refreshCache(@("TokenGroups"))
            '$secirc = new-object System.Security.Principal.IdentityReferenceCollection
            'foreach($sidByte in $ADObject.TokenGroups)
            '	{$secirc.Add((new-object System.Security.Principal.SecurityIdentifier $sidByte,0))}
            'foreach($grp in $secirc.Translate([System.Security.Principal.NTAccount]))
            '	{$arrGroups+=$grp.Value}
            '#Write-Host($arrGroups)
            'return ,$arrGroups

        Catch ex As Exception
            ErrorHandler("ERRORGetGroupsForADObjectold: " & CompDN, Err, ex)
            'GetGroupsForADObjectold = "".ToArray
            GetGroupsForADObjectold = GroupsForADObject.ToArray
            'GetGroupsForADObjectold = New String
        End Try

    End Function

    Public Overloads Function ConvertToOctetString(ByVal values As Byte()) As String
        Return ConvertToOctetString(values, False, False)
    End Function

    Public Overloads Function ConvertToOctetString(ByVal values As Byte(), ByVal isAddBackslash As Boolean) As String
        Return ConvertToOctetString(values, isAddBackslash, False)
    End Function

    Public Overloads Function ConvertToOctetString(ByVal values As Byte(), ByVal isAddBackslash As Boolean, ByVal isUpperCase As Boolean) As String

        Try

            Dim iterator As Integer
            Dim builder As StringBuilder

            Dim slash As String
            If isAddBackslash Then
                slash = "\"
            Else
                slash = String.Empty
            End If

            Dim formatCode As String
            If isUpperCase Then
                formatCode = "X2"
            Else
                formatCode = "x2"
            End If
            builder = New StringBuilder(values.Length * 2)
            For iterator = 0 To values.Length - 1
                builder.Append(slash)
                builder.Append(values(iterator).ToString(formatCode))
            Next

            Return builder.ToString()

        Catch ex As Exception
            ErrorHandler("ERRORConvertToOctetString:", Err, ex)
            ConvertToOctetString = ""
        End Try

    End Function

    Function GetRecentChangesForGroupNew(ByVal sGroupDN As String, ByVal intTimeDiffToCheck As Integer) As List(Of String)

        Dim RecentGroupChanges As New List(Of String)
        Dim intRangeFrom As Integer = 0
        Dim LastLoop As Boolean = False
        Dim QuitLoop As Boolean = False

        Try
            WriteLogEntry(vbTab & "Checking Group:" & vbTab & sGroupDN, 5)
            'Dim blnlastLoop As Boolean = False
            'lowRange = 0
            'highRange = lowRange + rangeStep

            Dim intRangeTo As Integer = intRangeFrom + ENTRIESTORETURNPERQUERY - 1


            'Dim commandPrefix As String = "<LDAP://" & sGroupDN & ">;(objectClass=*);member;range="
            'Dim commandSuffix As String = ";base"


            Dim ADGroup As New DirectoryEntry("LDAP://" & sGroupDN)
            'ADGroup.RefreshCache(New String() {METADATAAttribute})
            Dim sGroupName As String = ADGroup.Properties("Name").Value.ToString
            Dim PropColl As PropertyValueCollection = ADGroup.Properties(METADATAAttribute)


            Do While QuitLoop = False

                Dim CurrentRange As String

                'If blnlastLoop = True Then
                ' Perform this query with the "range=<lowRange>-*" range.
                'oComm.CommandText = commandPrefix & lowRange & "-*" & commandSuffix
                'CurrentRange = String.Format("{0};Range={1}-{2}", METADATAAttribute, intRangeFrom, "*")

                'Else
                ' Perform this query with the "range=<lowRange>-<highRange>" range.
                'oComm.CommandText = commandPrefix & lowRange & "-" & highRange & commandSuffix
                'CurrentRange = String.Format("{0};Range={1}-{2}", METADATAAttribute, intRangeFrom, intRangeTo)

                'CurrentRange = String.Format("member;range={0}-{1}", rangeLow, rangeHigh)
                'CurrentRange = String.Format("member;range={0}-*"  , rangeLow)
                If LastLoop = False Then
                    'CurrentRange = String.Format("member;range={0}-{1}", rangeLow, rangeHigh)
                    CurrentRange = String.Format("{0};Range={1}-{2}", METADATAAttribute, intRangeFrom, intRangeTo)
                Else
                    'CurrentRange = String.Format("member;range={0}-*", rangeLow)
                    CurrentRange = String.Format("{0};Range={1}-*", METADATAAttribute, intRangeFrom)
                End If

                'End If
                'Debug.Print("Current search command: " & oComm.CommandText)
                WriteLogEntry("CURRENTRANGE: " & CurrentRange, 15)

                ' Reset the retrieved members counter.
                'Dim IntNumRetrieved As Integer = 0

                Try
                    ADGroup.RefreshCache(New String() {CurrentRange})
                Catch e As Exception 'I might check for the expected hresult, but I don't know if I need to 
                    WriteLogEntry("ERROR IN METADATAGET2-Exiting Loop", 15)
                    'WriteLogEntry(e.Message & vbTab & e.InnerException.ToString, 10)
                    Exit Do
                End Try


                PropColl = ADGroup.Properties(METADATAAttribute)
                WriteLogEntry("####PropColl CAP " & PropColl.Capacity, 15)
                WriteLogEntry("#####PropColl COUNT " & PropColl.Count, 15)
                'If PropColl.Contains(CurrentRange) = True Then

                'WriteLogEntry("#####MEMNAME " & PropColl.Name, 1)
                For Each ReplValueMetaDataEntry As String In PropColl
                    'WriteLogEntry("#####MEMNAME " & ReplValueMetaDataEntry, 1)
                    'IntNumRetrieved += 1

                    'values.Add(propValue)
                    'MemberCount2 += 1
                    '********************************
                    '********************************
                    'TODO CHECK IF EXIST AND EXIT LOOP
                    'gets stuck in a loop if the range interval matches the number of members
                    '********************************
                    '********************************
                    Try

                        Dim XMLDoc As New Xml.XmlDocument
                        XMLDoc.LoadXml(ReplValueMetaDataEntry)
                        Dim sLastOrigChange As String = XMLDoc.DocumentElement.SelectSingleNode("ftimeLastOriginatingChange").InnerText
                        Dim dtmLastOrigChange As Date = CDate(sLastOrigChange)
                        Dim sMemberDN As String = XMLDoc.DocumentElement.SelectSingleNode("pszObjectDn").InnerText

                        WriteLogEntry(vbTab & "sMemberDN:" & vbTab & sMemberDN, 15)
                        WriteLogEntry(vbTab & "sLastOrigChange:" & vbTab & sLastOrigChange, 15)
                        WriteLogEntry(vbTab & "dtmLastOrigChange:" & vbTab & dtmLastOrigChange, 15)
                        Dim TimeDiff As TimeSpan = Now.Subtract(dtmLastOrigChange)

                        If TimeDiff.TotalMinutes <= intTimeDiffToCheck Then
                            If GetObjectClass(sMemberDN) = "computer" Then
                                WriteLogEntry(vbTab & vbTab & "COMPUTERCHANGEDGROUPS:" & vbTab & sMemberDN, 5)
                                RecentGroupChanges.Add(sMemberDN)
                            End If

                        End If
                    Catch ex2 As Exception
                        'WriteLogEntry("ERRORGETGROUPPROPS2: " & sGroupDN, 1)
                        ErrorHandler("ERROR GETGROUPPROPS2:", Err, ex2)
                    End Try

                Next
                If LastLoop = True Then QuitLoop = True
                'Else
                'LastLoop = True
                'End If

                'WriteLogEntry("blnlastLoop2: " & blnlastLoop, 1)

                'Iteration += 1
                'If blnlastLoop = True Then Exit Do

                'WriteLogEntry(vbTab & "IntNumRetrieved:" & vbTab & IntNumRetrieved, 5)

                'If PropColl.Count = ENTRIESTORETURNPERQUERY + 99999 Then
                ' blnlastLoop = True
                'intRangeFrom = intRangeTo + 1
                'Else

                'If we didn't return max records, go ahead and exit loop gracefully (will handle all but groups with more than 1000 easily)
                If PropColl.Count <> ENTRIESTORETURNPERQUERY Then
                    WriteLogEntry("Exiting gracefully" & vbTab & PropColl.Count & vbTab & ENTRIESTORETURNPERQUERY, 15)
                    QuitLoop = True
                    'Exit Do
                End If
                'If lastQuery = False Then
                'rangeLow = rangeHigh + 1
                'rangeHigh = rangeLow + (rangeStep - 1)
                'End If
                If LastLoop = False Then
                    'Keep going since there are more records to get
                    intRangeFrom = intRangeTo + 1
                    intRangeTo = intRangeTo + ENTRIESTORETURNPERQUERY
                    'End If
                End If

            Loop

            GetRecentChangesForGroupNew = RecentGroupChanges

        Catch ex As Exception
            'WriteLogEntry("ERROR GetGroupProps1: " & sGroupDN, 1)
            'WriteLogEntry(ex.Message & vbCrLf & ex.Source & vbCrLf & ex.InnerException.ToString, 1)
            ErrorHandler("ERROR GETGROUPPROPS1:", Err, ex)
            GetRecentChangesForGroupNew = RecentGroupChanges
        End Try
    End Function




    Function GetObjectClass(ByVal sObjectDN As String) As String
        Try

            Dim ADObject As New DirectoryEntry("LDAP://" & sObjectDN)
            GetObjectClass = ADObject.SchemaClassName
        Catch ex As Exception
            'TODO set as computer by default?
            GetObjectClass = ""
            ErrorHandler("ERROR GetObjectClass: " & sObjectDN, Err, ex)
        End Try

    End Function

    'Function FindGroupsModifiedRecently(ByVal sLDAPFromDate As String, ByVal intTZOffset As Integer, ByVal GroupExceptionsList As List(Of String)) As Dictionary(Of String, String)
    Function FindGroupsModifiedRecently(ByVal sLDAPFromDate As String,
                                        ByVal intTZOffset As Integer,
                                        ByVal GroupExceptionsList As List(Of String),
                                        ByVal GroupsToCollections As System.Collections.Specialized.NameValueCollection) As Dictionary(Of String, String)
        'Returns Dict with structure (ObjectName, ObjectDN)
        'Dim GroupsToCollections As New System.Collections.Specialized.NameValueCollection

        Try

            Dim RecentGroupsDict As New Dictionary(Of String, String)

            Dim ADRoot As New DirectoryEntry("LDAP://" & LDAPDOMAINNAME)
            Dim ADSearcher As New DirectorySearcher(ADRoot)
            ADSearcher.Filter = "(&(objectCategory=group)(whenChanged>=" & sLDAPFromDate & "))"


            ADSearcher.PropertiesToLoad.Add("distinguishedName")
            ADSearcher.PropertiesToLoad.Add("Name")
            ADSearcher.PropertiesToLoad.Add("whenChanged")
            Dim SearchResult As System.DirectoryServices.SearchResultCollection = ADSearcher.FindAll()
            'MsgBox("LDAP COUNT: " & colSearchResult.Count)
            'Debug.Print("LDAP COUNT: " & SearchResult.Count)
            WriteLogEntry(vbTab & "Number of Recent Group Changes:" & vbTab & SearchResult.Count, 1)
            For Each oGroup As SearchResult In SearchResult

                Dim sObjectDN As String = oGroup.Properties("distinguishedName")(0).ToString()
                Dim sObjectName As String = oGroup.Properties("Name")(0).ToString()
                Dim sObjectWhenChangedUTC As Date = CDate(oGroup.Properties("whenChanged")(0))
                Dim sObjectWhenChangedLocal As Date = sObjectWhenChangedUTC.AddMinutes(intTZOffset * -1)
                'MsgBox(sObjectDN & vbCrLf & sObjectName & vbCrLf & sObjectWhenChangedUTC & vbCrLf & sObjectWhenChangedLocal)


                ''#################''USE SMS COLL GROUPS INSTEAD OF EXCEPTIONS########################
                'GroupExceptionsList = List from text file - No Domain prefix
                'DictGroupsModifiedRecently = (NETBIOSDOMAINNAME & "\" & sObjectName, sObjectDN)  - Has Domain Prefix
                'GroupsToCollections = (GroupName, sCollectionID) - Has Domain prefix since the Collection query has the prefix
                ''If GroupsToCollections.Get(NETBIOSDOMAINNAME & "\" & sObjectName) Is Nothing Then

                '**************TODO - why did I switch this to .Get Is Nothing ???? Is backwards and failing
                'If GroupExceptionsList.Contains(sObjectName) Then
                'See if a Collection is using this Group
                If GroupsToCollections.Get(NETBIOSDOMAINNAME & "\" & sObjectName) Is Nothing Then
                    'Skipping group
                    'Debug.Print("SKIPGROUP" & vbTab & sObjectDN & vbTab & sObjectName & vbTab & sObjectWhenChangedUTC & vbTab & sObjectWhenChangedLocal)
                    WriteLogEntry(vbTab & "SKIPTHISGROUP-NOCOLLMATCH" & vbTab & sObjectDN & vbTab & sObjectName & vbTab & sObjectWhenChangedUTC & vbTab & sObjectWhenChangedLocal, 7)
                Else
                    'Add group to dictionary
                    'RecentGroupsDict.Add(sObjectName, sObjectDN)
                    'See if this group is in the Exception list
                    If GroupExceptionsList.Contains(sObjectName) Then
                        WriteLogEntry(vbTab & "SKIPTHISGROUP-GROUPEXCEPTION" & vbTab & sObjectDN & vbTab & sObjectName & vbTab & sObjectWhenChangedUTC & vbTab & sObjectWhenChangedLocal, 7)
                    Else
                        RecentGroupsDict.Add(NETBIOSDOMAINNAME & "\" & sObjectName, sObjectDN)

                        'Debug.Print("PROCESSGROUP" & vbTab & sObjectDN & vbTab & sObjectName & vbTab & sObjectWhenChangedUTC & vbTab & sObjectWhenChangedLocal)
                        WriteLogEntry(vbTab & "PROCESSGROUP" & vbTab & sObjectDN & vbTab & sObjectName & vbTab & sObjectWhenChangedUTC & vbTab & sObjectWhenChangedLocal, 7)
                    End If

                End If
            Next
            FindGroupsModifiedRecently = RecentGroupsDict
        Catch ex As Exception
            ErrorHandler("ERROR FindGroupsModifiedRecently:", Err, ex)
            FindGroupsModifiedRecently = New Dictionary(Of String, String)
        End Try

    End Function


    Function GetGroupExceptions() As List(Of String)

        Dim GroupExceptions As New List(Of String)
        Try

            If File.Exists(GROUPEXCEPTIONSFILE) = True Then
                Dim oReader As New System.IO.StreamReader(GROUPEXCEPTIONSFILE)

                Do While oReader.Peek() <> -1
                    Dim TextLine As String = oReader.ReadLine()
                    GroupExceptions.Add(TextLine)
                Loop
                oReader.Close()
                oReader = Nothing
            Else

            End If

        Catch ex As Exception
            ErrorHandler("ERROR GetGroupExceptions", Err, ex)
        End Try

        GetGroupExceptions = GroupExceptions
    End Function




    Function FindNewSMSClientRecords(ByVal dtmSMSRecordTimeStamp As Date) As Dictionary(Of String, String)
        Dim NewSMSClientRecords As New Dictionary(Of String, String)

        Try


            Dim sConnString As String = "Data Source=" & sSQLServerName & ";Initial Catalog=" & sSQLServerDB & ";Integrated Security=True"
            'Dim sSQLQuery As String = "SELECT TOP 5 * FROM v_R_System"
            'Dim sSQLQuery As String = "SELECT TOP 2 * FROM v_R_System WHERE Creation_Date0 > '2009-10-28 14:00' ORDER BY Name0 DESC"
            'Dim sSQLQuery As String = "SELECT Name0 FROM v_R_System WHERE Creation_Date0 > '" & dtmSMSRecordTimeStamp & "'"
            Dim sSQLQuery As String = "SELECT DISTINCT Name0 FROM v_R_System WHERE Creation_Date0 > '" & dtmSMSRecordTimeStamp & "' AND Resource_Domain_OR_Workgr0 = '" & NETBIOSDOMAINNAME & "'"
            'Resource_Domain_OR_Workgr0
            Dim SQLConn As New SqlConnection()
            Dim SQLCmd As New SqlCommand()
            SQLConn.ConnectionString = sConnString
            SQLConn.Open()
            SQLCmd.Connection = SQLConn
            SQLCmd.CommandText = sSQLQuery
            Dim SQLReader As SqlClient.SqlDataReader = SQLCmd.ExecuteReader
            While SQLReader.Read
                Dim sShortName As String = SQLReader("Name0").ToString
                WriteLogEntry(vbTab & vbTab & "NewClient:" & vbTab & sShortName, 10)
                Dim sObjectDN As String = GetDistinguishedName(sShortName & "$")
                If sObjectDN <> "" Then
                    'Only add to Dict if we find it in AD
                    NewSMSClientRecords.Add(sObjectDN, sShortName)
                End If
            End While

            FindNewSMSClientRecords = NewSMSClientRecords
        Catch ex As Exception
            ErrorHandler("ERROR FindNewSMSClientRecords", Err, ex)
            FindNewSMSClientRecords = New Dictionary(Of String, String)
        End Try

    End Function
    Function GetDistinguishedName(ByVal sObjectShortName As String) As String

        Try

            Dim ADRoot As New DirectoryEntry("LDAP://" & LDAPDOMAINNAME)
            Dim ADSearcher As New DirectorySearcher(ADRoot)
            Dim sObjectDN As String
            ADSearcher.Filter = "(&(objectClass=Computer)(samAccountName=" & sObjectShortName & "))"
            ADSearcher.PropertiesToLoad.Add("distinguishedName")
            Dim ADObject As System.DirectoryServices.SearchResult = ADSearcher.FindOne()
            'If IsDBNull(ADObject) = False Then
            'Check to see if we found the computer in AD
            If ADObject Is Nothing Then
                WriteLogEntry(vbTab & vbTab & "WARNING: Cannot find object in AD: " & sObjectShortName, 1)
                sObjectDN = ""
            Else
                sObjectDN = ADObject.GetDirectoryEntry.Properties("DistinguishedName").Value.ToString
            End If

            GetDistinguishedName = sObjectDN
        Catch ex As Exception
            ErrorHandler("ERROR GetDistinguishedName:" & sObjectShortName, Err, ex)
            GetDistinguishedName = ""
        End Try

    End Function



    'Function CreateADGroupDiscDDR(ByRef oDDR As SMSResGen, ByVal sObjectDN As String) As Boolean
    Function CreateADGroupDiscDDR(ByVal sObjectDN As String, ByRef GroupsArrList As ArrayList, ByRef OUsArrList As ArrayList) As Boolean
        'Gets all info about the Computer object and creates a DDR
        'Also adds groups to GroupsArrList
        'Also adds OUs to OUsArrList
        'Both are returned to the main loop for processing

        Dim sObjectCN As String = ""
        Try
            Dim oADObject As New System.DirectoryServices.DirectoryEntry
            Dim sDDRPath As String
            Dim oComputerToProcess As New ComputerToProcess

            'WriteLogEntry("DDR2: " & sObjectShortname, 1)

            'Get ObjectDN
            'Dim sObjectDN As String = GetDistinguishedName(sObjectShortname & "$")
            WriteLogEntry(vbTab & "DDRDN: " & sObjectDN, 5)
            'blnAccountDisabled = False
            'oADObject = GetObject("LDAP://" + sObjectDN)
            'sObjectCN = oADObject.CN
            oADObject.Path = "LDAP://" + sObjectDN
            sObjectCN = oADObject.Properties("CN").Value.ToString
            WriteLogEntry(vbTab & "DDRCN: " & sObjectCN, 5)

            'Check if Disabled
            'int userAccountControl = Convert.ToInt32(result.Properties["userAccountControl"][0]); 
            '            string samAccountName = Convert.ToString(result.Properties["samAccountName"][0]); 
            '            bool disabled = ((userAccountControl & 2) > 0);
            'Dim sUserAccountsControl As String = oADObject.Properties("userAccountControl").Value.ToString
            Dim intUserAccountControl As Integer = Convert.ToInt32(oADObject.Properties("userAccountControl").Value)
            'Dim blnAccountDisabled As Boolean
            ' WriteLogEntry(vbTab & vbTab & "sUserAccountsControl: " & sUserAccountsControl, 5)
            'WriteLogEntry(vbTab & vbTab & "intUserAccountControl: " & intUserAccountControl, 5)
            'WriteLogEntry(vbTab & vbTab & (intUserAccountControl And 2), 5)
            'WriteLogEntry(vbTab & vbTab & CBool((intUserAccountControl And 2)), 5)
            If CBool((intUserAccountControl And 2)) = True Then
                'blnAccountDisabled = True
                CreateADGroupDiscDDR = True
                WriteLogEntry(vbTab & "AD Computer Account Disabled - Skipping: " & sObjectDN, 5)
            Else

                oADObject.RefreshCache(New String() {"CanonicalName"})
                'WriteLogEntry("VT:" & VarType(oADObject.Properties("CanonicalName")).ToString, 1)
                'WriteLogEntry("ISARR: " & IsArray(oADObject.Properties("CanonicalName")).ToString, 1)
                Dim sObjectCanonicalName As String = oADObject.Properties("CanonicalName").Value.ToString

                oComputerToProcess.DN = sObjectDN
                oComputerToProcess.CN = sObjectCN
                oComputerToProcess.canonicalName = sObjectCanonicalName

                Dim ObjectGroups As Array = GetGroupsForADObjectold(sObjectDN)
                oComputerToProcess.Groups = ObjectGroups

                '##Add computer groups to full group list##
                GroupsArrList.AddRange(ObjectGroups)

                Dim ObjectOUs As Array = CanonicalSplitter(sObjectCanonicalName)
                oComputerToProcess.OUs = ObjectOUs
                'Dim ObjectContainers(-1) As String  'Empty Array since we don't need any values for this

                '##Add computer OUs to full OU list##
                OUsArrList.AddRange(ObjectOUs)

                oComputerToProcess.MaxGroupLength = GetDDRLength(ObjectGroups)
                oComputerToProcess.MaxOULength = GetDDRLength(ObjectOUs)
                'oComputerToProcess.MaxContainersLength = oComputerToProcess.MaxOULength
                'oComputerToProcess.MaxContainersLength = GetDDRLength(ObjectContainers) 'Empty

                sDDRPath = My.Computer.FileSystem.CombinePath(DDRFOLDER, "AD" + sObjectCN + ".DDR")


                'TODO Try/Catch
                '##Delete old DDR if it exists##
                If File.Exists(sDDRPath) = True Then
                    WriteLogEntry(vbTab & "DDR Exists (DELETING NOW): " & sDDRPath, 7)
                    File.Delete(sDDRPath)
                Else
                    WriteLogEntry(vbTab & "DDR Does not Exist: " & sDDRPath, 7)
                End If


                WriteLogEntry(vbTab & "Creating DDR: " & sDDRPath, 5)
                oDDR.DDRNew("System", "SMS_AD_SYSTEM_GROUP_DISCOVERY_AGENT", SMSSITECODE)
                oDDR.DDRAddString("NetBIOS Name", sObjectCN, 32, ADDPROP_KEY)
                '#DDR.DDRAddString("Operating System Name and Version", "Microsoft Windows NT Workstation 5.11", 64, 1)	#Don't seem to need this
                oDDR.DDRAddString("Resource Domain OR Workgroup", NETBIOSDOMAINNAME, 32, ADDPROP_NONE)

                'This is the part we have to disable "Option Strict On" for since the SDK DLL does not contain all DDR properties
                '#######################################
                oDDR.DDRAddStringArray("System Group Name", ObjectGroups, oComputerToProcess.MaxGroupLength, CType(17, DDRPropertyFlagsEnum))
                oDDR.DDRAddStringArray("System OU Name", oComputerToProcess.OUs, oComputerToProcess.MaxOULength, CType(17, DDRPropertyFlagsEnum))
                'Maybe don't need Container Name anymore with SCCM. Not all records have this with normal Disc anymore
                'oDDR.DDRAddStringArray("System Container Name", oComputerToProcess.OUs, oComputerToProcess.MaxOULength, 17)
                '#######################################

                '##Write the DDR to the Temp folder##
                oDDR.DDRWrite(sDDRPath)

                '##Copy DDR to Primary Auth\DDM.box folder##
                If blnTestMode = False Then

                    WriteLogEntry(vbTab & "Copying DDR to Primary:", 7)
                    WriteLogEntry(vbTab & vbTab & "From: " & sDDRPath, 7)
                    WriteLogEntry(vbTab & vbTab & "To: " & DDMBOXFOLDER & "\" & "AD" + sObjectCN + ".DDR", 7)
                    File.Copy(sDDRPath, DDMBOXFOLDER & "\" & "AD" + sObjectCN + ".DDR", True)
                Else
                    WriteLogEntry(vbTab & "TESTMODE-Skipping DDR Copy", 7)
                End If

                CreateADGroupDiscDDR = True
            End If
        Catch ex As Exception
            CreateADGroupDiscDDR = False
            ErrorHandler("ERRORDDRMAIN: " & sObjectDN, Err, ex)
        End Try

    End Function

    Function GetDDRLength(ByVal TempArray As Array) As Integer
        'Determine what length we need to specify in the DDR for this attribute

        Dim LongestDDREntry As Integer = 0
        Dim LongestDDRToReturn As Integer = 0
        Try

            For Each DDREntry As String In TempArray
                'Debug.Print("DDRLENGTH:" & DDREntry & vbTab & DDREntry.Length.ToString)
                If DDREntry.Length > LongestDDREntry Then LongestDDREntry = DDREntry.Length
            Next
            'Debug.Print("LongestDDREntry: " & LongestDDREntry.ToString)

            'LongestDDRToReturn = (CInt(LongestDDREntry / 32) + 1) * 32
            LongestDDRToReturn = ((CInt(Int(LongestDDREntry / 32)) * 32) + 32)

            'Debug.Print("LongestDDRToReturn: " & LongestDDRToReturn.ToString)
            GetDDRLength = LongestDDRToReturn
        Catch ex As Exception
            GetDDRLength = 160
        End Try

    End Function


    Function CanonicalSplitter(ByVal sObjectCN As String) As Array

        Dim AllCNs As New ArrayList
        Try

            Dim arrIndividualOUs() As String = sObjectCN.ToString.Split("/"c)
            Dim intOUCounter As Integer = 1
            Dim intUpperBound As Integer = arrIndividualOUs.GetUpperBound(0)
            Do While intOUCounter < intUpperBound
                Dim sIndividualOU As New StringBuilder

                For i As Integer = 0 To intOUCounter
                    sIndividualOU.Append(arrIndividualOUs(i))
                    If i <> intOUCounter Then   'Don't append the delimiter if it is the last entry
                        sIndividualOU.Append("/")
                    End If
                Next

                Debug.Print("###" & sIndividualOU.ToString)
                AllCNs.Add(sIndividualOU.ToString)
                intOUCounter = intOUCounter + 1
            Loop

            CanonicalSplitter = AllCNs.ToArray
        Catch ex As Exception
            ErrorHandler("ERROR CanonicalSplitter: " & sObjectCN, Err, ex)
            'CanonicalSplitter = "".ToArray
            CanonicalSplitter = AllCNs.ToArray
        End Try

    End Function



    Private Class ComputerToProcess
        Public DN As String
        Public CN As String
        Public canonicalName As String
        Public Groups As Array
        Public OUs As Array
        'Public Containers As Array
        Public MaxGroupLength As Integer
        Public MaxOULength As Integer
        'Public MaxContainersLength As Integer

    End Class

    Private Sub Timer1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs)

    End Sub

    Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs)

    End Sub

    Public Function GetSettingsFromReg() As Boolean

        Try

            SMSSERVER = GetRegValue(strRegKey, "SMSServer")
            SMSSITECODE = GetRegValue(strRegKey, "SMSSITECODE")
            NETBIOSDOMAINNAME = GetRegValue(strRegKey, "NETBIOSDOMAINNAME")
            LDAPDOMAINNAME = GetRegValue(strRegKey, "LDAPDOMAINNAME")

            ADDITIONALMINUTES = CInt(GetRegValue(strRegKey, "ADDITIONALMINUTES"))
            SECONDSBETWEENLOOPS = CInt(GetRegValue(strRegKey, "SECONDSBETWEENLOOPS"))
            MINUTESBACKFORNEWSMSCLIENTS = CInt(GetRegValue(strRegKey, "MINUTESBACKFORNEWSMSCLIENTS"))

            ENTRIESTORETURNPERQUERY = CInt(GetRegValue(strRegKey, "ENTRIESTORETURNPERQUERY"))
            intDebugLevel = CInt(GetRegValue(strRegKey, "DEBUGLEVEL"))
            sLogPath = GetRegValue(strRegKey, "LOGPATH")
            sLogFileName = Now.ToString("yyyyMMdd") & ".log"
            LogFileFullPath = sLogPath & "\" & sLogFileName

            DDRFOLDER = GetRegValue(strRegKey, "DDRFOLDER")
            GROUPEXCEPTIONSFILE = GetRegValue(strRegKey, "GROUPEXCEPTIONSFILE")
            TIMEDIFFFILE = GetRegValue(strRegKey, "TIMEDIFFFILE")
            DDMBOXFOLDER = GetRegValue(strRegKey, "DDMBOXFOLDER")

            blnTestMode = CBool(GetRegValue(strRegKey, "TESTMODE"))

            GetSettingsFromReg = True

            'TODO FIGURE OUT WHICH VARIABLES TO TREAT LIKE THIS FOR THIS APP

            ''Check to make sure we got valid setting from registry
            ''For certain values, just use defaults if we can't find them
            ''Return a failure for values needed for the app to run
            'If intTimerInterval = Nothing Then intTimerInterval = 300
            'If intDebugLevel = Nothing Then intDebugLevel = 3
            'If intLeaveRemoteFiles = Nothing Then intLeaveRemoteFiles = 0
            'If intAdvTimeUntil = Nothing Then intAdvTimeUntil = 20
            'If intAdvTimeAfter = Nothing Then intAdvTimeAfter = 5
            'If blnDayFilterEnabled = Nothing Then blnDayFilterEnabled = False
            'If blnTimeFilterEnabled = Nothing Then blnTimeFilterEnabled = False

            'If strSMSServer = Nothing Then GetSettingsFromReg = False
            'If strSMSSite = Nothing Then GetSettingsFromReg = False
            'If strLogFile = Nothing Then GetSettingsFromReg = False
            'If strServerColl = Nothing Then GetSettingsFromReg = False
            'If strWOLExe = Nothing Then GetSettingsFromReg = False
            'If strWOLExeSourceFolder = Nothing Then GetSettingsFromReg = False
            'If strRmtWOLFolder = Nothing Then GetSettingsFromReg = False
            'If strSubnetSourceFolder = Nothing Then GetSettingsFromReg = False
            'If strRemServiceExePath = Nothing Then GetSettingsFromReg = False
            'If WMIPath = Nothing Then GetSettingsFromReg = False

            'get the current date and use it for the logfile name
            'sLogFile = sLogFile.TrimEnd(CChar("\")) & "\" & Now.ToString("yyyyMMdd") & ".log"

        Catch ex As Exception
            Call ErrorHandler("ERROR - Couldn't get registry settings", Err, ex)
            GetSettingsFromReg = False
        End Try

    End Function

    Public Function EvalExpired() As Boolean

        Try
            'Set Expired to False and exit function if this isn't an eval
            If blnCustomerVersion = True Then
                EvalExpired = False
                Exit Function
            End If
            EvalExpired = False
            Dim intEvalDateDiff As Long = DateDiff(DateInterval.Day, dtmEvalExpireDate, Now)
            If intEvalDateDiff > 0 Then
                EvalExpired = True
            Else
                EvalExpired = False
            End If
        Catch ex As Exception
            Call ErrorHandler("ERROR - Problem checking whether Evaluation version is expired", Err, ex)
            EvalExpired = True
        End Try

    End Function
    Function GetTimeSinceLastRun() As Integer
        'Defaults to 5 hours if we can't figure out when it last ran

        Dim TextLine As String
        Dim dtmLastRun As Date
        Dim TimeDiff As Integer

        Try

            If File.Exists(TIMEDIFFFILE) = True Then

                Dim oReader As New System.IO.StreamReader(TIMEDIFFFILE)

                TextLine = oReader.ReadLine()
                dtmLastRun = CDate(TextLine)
                TimeDiff = CInt(Now.Subtract(dtmLastRun).TotalMinutes)
                oReader.Close()
                oReader = Nothing

            Else
                TimeDiff = 300
            End If

            'TODO Don't update TimeDiff file unless we have some success (figure out what we need to trap on)
            Dim oWriter As New System.IO.StreamWriter(TIMEDIFFFILE)
            oWriter.WriteLine(Now.ToString)
            oWriter.Close()
            oWriter = Nothing

            GetTimeSinceLastRun = TimeDiff
        Catch ex As Exception
            ErrorHandler("ERROR GetTimeSinceLastRun: " & TIMEDIFFFILE, Err, ex)
            GetTimeSinceLastRun = 300
        End Try
    End Function

    Function GetTZOffset() As Integer

        Try

            Dim LocalTimeZone As TimeZone = System.TimeZone.CurrentTimeZone
            Dim TZTimeOffset As TimeSpan = LocalTimeZone.GetUtcOffset(Now)
            Dim intTZTimeOffset As Integer = CInt(TZTimeOffset.TotalMinutes * -1)
            GetTZOffset = intTZTimeOffset
        Catch ex As Exception
            'If this errors, set to 99999999 so we can trap on that in the main function
            ErrorHandler("ERROR GetTZOffset", Err, ex)
            GetTZOffset = 99999999

        End Try
    End Function


    Public Function GetRegValue(ByVal sKey As String, ByRef sValue As String) As String

        Try
            Dim oRegKey As RegistryKey = Registry.LocalMachine.OpenSubKey(strRegKey)
            GetRegValue = CStr(oRegKey.GetValue(sValue))
            oRegKey.Close()
        Catch ex As Exception
            Call ErrorHandler("ERROR - Couldn't get registry value: " & sKey & "\" & sValue, Err, ex)
            GetRegValue = ""
        End Try

    End Function

    Public Function ErrorHandler(ByVal sErrorInfo As String, ByRef errObject As ErrObject, ByRef ex As Exception) As Boolean


        Dim sErrorDetails As String = ""
        Try
            WriteLogEntry(sErrorInfo, 1)
            sErrorDetails = "ERROR details: " & _
            vbCrLf & StrDup(4, vbTab) & "Line: " & errObject.Erl & _
            vbCrLf & StrDup(4, vbTab) & "Source: " & errObject.Source & _
            vbCrLf & StrDup(4, vbTab) & "Error Number: " & errObject.Number & _
            vbCrLf & StrDup(4, vbTab) & "Description: " & errObject.Description & _
            vbCrLf & StrDup(4, vbTab) & "Exception Msg: " & ex.Message & _
            vbCrLf & StrDup(4, vbTab) & "StackTrace: " & ex.StackTrace
            WriteLogEntry(sErrorDetails, 1)
            ErrorHandler = True
        Catch ex2 As Exception
            WriteToEventLog("Couldn't log error to logfile:" & vbCrLf & sErrorInfo & vbCrLf & _
                sErrorDetails, EventLogEntryType.Error, 2000)
            ErrorHandler = False
        End Try

    End Function
    Public Function WriteLogEntry(ByVal sLogText As String, ByVal intErrorLevel As Integer) As Boolean

        Try
            If intErrorLevel <= intDebugLevel Then
                'Only log events specified by the DebugLevel in the registry
                oLogFile.WriteLine(Now & vbTab & sLogText)
                oLogFile.Flush()
            End If
            WriteLogEntry = True
        Catch ex As Exception

            'Call ErrorHandler("ERROR - Couldn't write log entry", Err, ex)
            Dim sErrorInfo As String = "ERROR - Couldn't write log entry"
            Dim sErrorDetails As String = "ERROR details: " & _
            vbCrLf & StrDup(4, vbTab) & "Line: " & Err.Erl & _
            vbCrLf & StrDup(4, vbTab) & "Source: " & Err.Source & _
            vbCrLf & StrDup(4, vbTab) & "Error Number: " & Err.Number & _
            vbCrLf & StrDup(4, vbTab) & "Description: " & Err.Description & _
            vbCrLf & StrDup(4, vbTab) & "Exception Msg: " & ex.Message & _
            vbCrLf & StrDup(4, vbTab) & "StackTrace: " & ex.StackTrace
            Call WriteToEventLog("Couldn't log error to logfile:" & vbCrLf & sLogText & vbCrLf & sErrorInfo & vbCrLf & _
                sErrorDetails, EventLogEntryType.Error, 2000)
            'oLogFile.Dispose()
            'Finally
            '   oLogFile.Dispose()
            WriteLogEntry = False
        End Try


    End Function

    Public Sub WriteToEventLog(ByVal sEventText As String, ByVal EntryType As EventLogEntryType, ByVal intEventNumber As Integer)

        Try
            Dim MyLog As New EventLog   ' create a new event log 
            MyLog.Source = "SMSCollectionSpeedup"
            MyLog.WriteEntry(sEventText, EntryType, intEventNumber)
        Catch ex As Exception
            'Do nothing...if we can't log here either, all logging is most likely failing
        End Try

    End Sub
End Class

'################################################################################
'Test/Old routines
'################################################################################

'Function UpdateCollections(ByVal CollectionsToUpdate As Dictionary(Of String, String)) As Boolean

'    Try

'        'Microsoft.ConfigurationManagement.ManagementProvider.
'        'System.Management.Instrumentation.

'        Dim myConnectionOptions As New ConnectionOptions
'        'With myConnectionOptions
'        '    .Impersonation = System.Management.ImpersonationLevel.Impersonate
'        '    '* Use next line for XP
'        '    .Authentication = System.Management.AuthenticationLevel.Packet
'        '    '* Use next line for Win prior XP
'        '    '*.Authentication = System.Management.AuthenticationLevel.Connect
'        'End With
'        Dim sScopePath As String = "\\" & SMSSERVER & "\root\sms\site_" & SMSSITECODE
'        Debug.Print(sScopePath)
'        Dim myManagementScope As ManagementScope = New ManagementScope(sScopePath, myConnectionOptions)
'        '* Replace the "." with an actual servername for remote connection
'        'Dim myServerName As String = "."
'        'myManagementScope = New System.Management.ManagementScope("\\" & SMSSERVER & "\root\sms\site_" & SMSSITECODE, myConnectionOptions)
'        'myManagementScope = New System.Management.ManagementScope("\\" & SMSSERVER & "\root\sms\site_" & SMSSITECODE)
'        '* connect to WMI namespace
'        myManagementScope.Connect()
'        If myManagementScope.IsConnected Then
'            'Dim managementClass As New ManagementClass("SMS_Collection")
'            For Each sCollectionID As String In CollectionsToUpdate.Keys
'                'Dim Collection As ManagementObject
'                'managementClass.Item(
'                ' Dim  Coll as  ManagementObject("SMS_Collection.CollectionID=" & strSingleQuote & oItem.CollectionID & strSingleQuote)
'                Debug.Print(sCollectionID)
'                'Dim Collection As New ManagementObject("SMS_Collection.CollectionID='" & sCollectionID & "'")
'                'Dim Collection As New ManagementObject(myManagementScope, New ManagementPath("SMS_Collection.CollectionID='xxxxxxxx'"), Nothing)
'                Dim Collection As New ManagementObject(myManagementScope, New ManagementPath("SMS_Collection.CollectionID='" & sCollectionID & "'"), Nothing)

'                'Dim coll2 As New IResultObject

'                Collection.Get()
'                'Dim sCollRuleName As String = Collection.GetPropertyValue("RuleName").ToString
'                'Debug.Print(vbTab & sCollRuleName)

'                'Collection.RequestRefresh()
'                Dim inPARAM As ManagementBaseObject
'                inPARAM = Collection.GetMethodParameters("RequestRefresh")
'                inPARAM("IncludeSubcollections") = False
'                Collection.InvokeMethod("RequestRefresh", inPARAM, Nothing)


'                Dim CollRules As Array = CType(Collection.GetPropertyValue("CollectionRules"), Array)

'                For Each Rule As Object In CollRules
'                    'Debug.Print(Rule.RuleName().tostring)
'                    Debug.Print(VarType(Rule).ToString)
'                Next
'                'Dim CollRules As PropertyData = Collection.Properties.Item("CollectionRules")

'                'For Each blah As QualifierData In CollRules.Qualifiers
'                'Debug.Print(blah.Value.ToString)
'                'Next

'                'Prop = Collection.Properties_.Item("CollectionRules")
'                'If Not Prop.Value Is System.DBNull.Value Then Rules = Prop.Value

'                'If Not IsNothing(Rules) Then


'                'Dim RuleSet() As Object




'                'RuleSet = Collection.Properties("CollectionRules").Value

'                'Dim CollRules As Object = Collection.GetPropertyValue("CollectionRules")
'                'Debug.Print(CollRules.GetType().ToString)
'                'Dim CollRules As PropertyDataCollection = Collection.Properties("CollectionRules")
'                'Dim CollRules As PropertyData = Collection.Properties("CollectionRules")
'                'Dim CollRules As PropertyDataCollection = Collection.Properties("CollectionRules")
'                'Dim CollRule As PropertyData = Collection.Properties("CollectionRules")
'                'For Each CollRule In CollRules
'                'Console.WriteLine("RuleName = " & CollRule.Name)
'                'Next ' diskProperty

'                'For Each Rule As Object In CollRules

'                'Next


'                'Collection.InvokeMethod("RequestRefresh", inPARAM, Nothing)

'                'bind our management object
'                'disk.Get();

'            Next

'            'instCollection = oWMI.Get("SMS_Collection.CollectionID=" & strSingleQuote & oItem.CollectionID & strSingleQuote)
'        Else
'            Debug.Print("NOT CONNECTED")
'        End If

'    Catch ex As Exception

'    End Try

'End Function


'Public Function GetGroupsForADObjectNew(ByVal sObjectName As String) As Array
'    'This way is actually slower than doing the work ourself
'    'Public Function GetGroupsForADObject() As PrincipalSearchResult(Of Principal)
'    'Usage
'    Dim GroupsForADObject As New ArrayList
'    WriteLogEntry("GETGROUPS:" & vbTab & sObjectName, 1)
'    'Dim instance As UserPrincipal
'    'Dim returnValue As PrincipalSearchResult(Of Principal)

'    'returnValue = instance.GetAuthorizationGroups(
'    ''''''
'    'Dim name As String = Request.ServerVariables("LOGON_USER")
'    'Dim user As UserPrincipal = UserPrincipal.FindByIdentity(New PrincipalContext(ContextType.Domain), "ds\mbroad")
'    Dim user As UserPrincipal = UserPrincipal.FindByIdentity(New PrincipalContext(ContextType.Domain), IdentityType.DistinguishedName, sObjectName)
'    'Dim user As UserPrincipal = UserPrincipal.FindByIdentity(New PrincipalContext(ContextType.Domain), sObjectName)
'    WriteLogEntry("1", 1)
'    Dim groups As PrincipalSearchResult(Of Principal) = user.GetAuthorizationGroups()
'    WriteLogEntry("2", 1)

'    For Each ogroup As Principal In groups
'        WriteLogEntry("*GROUP*", 1)
'        WriteLogEntry(vbTab & ogroup.Name(), 1)
'        'ogroup.Name()
'        GroupsForADObject.Add(NETBIOSDOMAINNAME & "\" & ogroup.Name())
'    Next
'    GetGroupsForADObjectNew = GroupsForADObject.ToArray
'End Function


'Function GetRecentChangesForGroup(ByVal sGroupDN As String, ByVal intTimeDiffToCheck As Integer) As List(Of String)

'    Try

'        Dim RecentGroupChanges As New List(Of String)
'        Dim intRangeFrom As Integer = 0
'        Dim intRangeTo As Integer = intRangeFrom + ENTRIESTORETURNPERQUERY - 1
'        Try
'            Dim ADGroup As New DirectoryEntry("LDAP://" & sGroupDN)
'            ADGroup.RefreshCache(New String() {METADATAAttribute})
'            Dim sGroupName As String = ADGroup.Properties("Name").Value.ToString
'            Dim PropColl As PropertyValueCollection = ADGroup.Properties(METADATAAttribute)

'            WriteLogEntry(sGroupName & " Group Member Metadata COUNT: " & PropColl.Count, 3)

'            Dim Iteration As Integer
'            Dim MemberCount2 As Integer

'            Do
'                intRangeFrom = Iteration * ENTRIESTORETURNPERQUERY
'                intRangeTo = (Iteration + 1) * ENTRIESTORETURNPERQUERY - 1
'                WriteLogEntry("RANGEFROM/TO: " & intRangeFrom & vbTab & intRangeTo, 1)
'                If MemberCount2 <> intRangeFrom Then Exit Do 'Exit the loop if we aren't finding new records anymore
'                'What if we have exacty 1000 records? or 2000

'                'This is the attribute ranging method for retrieving the contents of large attributes 
'                Dim CurrentRange As String
'                CurrentRange = String.Format("{0};Range={1}-{2}", METADATAAttribute, intRangeFrom, intRangeTo)
'                'CurrentRange = String.Format("{0};Range={1}-{2}", METADATAAttribute, 5000, 5500)
'                'CurrentRange = String.Format("member;range={0}-{1}", intRangeFrom, intRangeTo)
'                'MsgBox(CurrentRange)
'                WriteLogEntry("CURRENTRANGE: " & CurrentRange, 1)
'                WriteLogEntry("MemberCount2: " & MemberCount2, 1)
'                'this will throw when the lower bound on the range is too high() 
'                Try
'                    ADGroup.RefreshCache(New String() {CurrentRange})
'                Catch e As Exception 'I might check for the expected hresult, but I don't know if I need to 
'                    WriteLogEntry("ERROR IN METADATAGET2", 1)
'                    Exit Do

'                End Try
'                'Get the values for for the current range of attributes 
'                PropColl = ADGroup.Properties(METADATAAttribute)
'                WriteLogEntry("#### " & PropColl.Capacity, 1)
'                For Each ReplValueMetaDataEntry As String In PropColl
'                    'values.Add(propValue)
'                    MemberCount2 += 1
'                    '********************************
'                    '********************************
'                    'TODO CHECK IF EXIST AND EXIT LOOP
'                    'gets stuck in a loop if the range interval matches the number of members
'                    '********************************
'                    '********************************

'                    Dim XMLDoc As New Xml.XmlDocument
'                    XMLDoc.LoadXml(ReplValueMetaDataEntry)
'                    Dim sLastOrigChange As String = XMLDoc.DocumentElement.SelectSingleNode("ftimeLastOriginatingChange").InnerText
'                    Dim dtmLastOrigChange As Date = CDate(sLastOrigChange)
'                    Dim sMemberDN As String = XMLDoc.DocumentElement.SelectSingleNode("pszObjectDn").InnerText
'                    'Debug.Print("MEMBERCOUNT2: " & MemberCount2)
'                    'Debug.Print("CUSTOM: " & sLastOrigChange)
'                    'Debug.Print("CUSTOM2: " & dtmLastOrigChange)
'                    'Debug.Print("CUSTOM3: " & sMemberDN)
'                    Dim TimeDiff As TimeSpan = Now.Subtract(dtmLastOrigChange)

'                    'Debug.Print("CUSTOM4: " & TimeDiff.TotalMinutes)
'                    If TimeDiff.TotalMinutes <= intTimeDiffToCheck Then
'                        'Debug.Print("OBJECTCHANGEDGROUPS: " & sMemberDN)
'                        If GetObjectClass(sMemberDN) = "computer" Then
'                            'Debug.Print("COMPUTERCHANGEDGROUPS: " & sMemberDN)
'                            WriteLogEntry(vbTab & "COMPUTERCHANGEDGROUPS:" & vbTab & sMemberDN, 5)
'                            RecentGroupChanges.Add(sMemberDN)
'                        End If

'                    End If

'                Next
'                Iteration += 1
'            Loop
'        Catch ex As Exception
'            ErrorHandler("ERROR GetRecentChangesForGroup:", Err, ex)
'            GetRecentChangesForGroup = New List(Of String)

'        End Try


'        GetRecentChangesForGroup = RecentGroupChanges
'    Catch ex As Exception
'        ErrorHandler("ERROR GetRecentChangesForGroup:", Err, ex)
'        GetRecentChangesForGroup = New List(Of String)
'    End Try

'End Function